Adding RESTful APIs to Portlets
RESTful APIs, while not new, have only recently seen adoption in portlets. Why? They are often used to expose a service. In the case of portlets, there is often a more complete service that the portlet fronts. Another common use is to provide the back end of single page applications. It is this later use that is likely to drive more RESTful APIs in portlets.
Luckily, most portlets use Springframework with it's vast array of complimentary libraries. Springframework Web Services is one such library that directly supports implementing RESTful APIs.
Covering how to create a portlet is beyond the scope of this post, so we assume a refactor of adding a RESTful API.
Let's get started.
Spring 4.x and Jackson
The @RestController was introduced in Spring 4. This is a specialized version of @Controller that expects to return JSON or XML instead of redirecting to a view. Rev Spring to a 4.0 or later version in your pom.xml
file.
<spring.version>4.2.5.RELEASE</spring.version>
For marshalling POJOs into JSON, you will need Jackson. Make sure you have the dependencies in your pom.xml
.
<jackson.version>2.7.3</jackson.version>
...
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
DispatcherServlet
In many portlets, there is no need for Dispather Servtlet in web.xml
. The portal handles requests and dispatches them through the portlet interface. For a RESTful API, a servlet is needed in the portlet to handle direct requests. The following need to be included in web.xml
:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
...
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/v1/mail/*</url-pattern>
</servlet-mapping>
Servlet Context
If the above was needed, then the supporting context file is also likely needed. Spring takes the name, spring
, and looks for a context file like WEB-INF/spring-servlet.xml
. If the servlet was named api, then Spring would look for WEB-INF/api-servlet.xml
. In the appropriate file, you should have the following:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="... TestRestController"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" id="handlerMapping">
<property name="alwaysUseFullPath" value="true"/>
</bean>
<mvc:annotation-driven/>
</beans>
This will cause Spring to scan the specified bean/class for the controller annotations in our Java code.
Java REST Controller
Finally, it is time to work on the Java controller. We will create a simple test controller. Create a Java file in your portlet project. Location is not important. Spring is going to scan all the Java classes for the controller annotations. In the source file, include the following:
package ...
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@RestController
@ReqestMapping("/v1/api")
public final class TestRestController {
@RequestMapping("test")
public Message test(@RequestParam(value="name", defaultValue = "Me") String name) {
Message msg = new Message();
msg.setText("testing " + name);
return msg;
}
@XmlRootElement
public final static class Message {
String text;
@XmlElement
public String getText() {return text;}
public void setText(String text) { this.text = text; }
}
}
That's it for our code! In this test we are using a static inner class to demonstrate the marshaling of a POJO to JSON. Note the JAXB XML annotations. We will return to them when we test.
Testing It Out
Now deploy and test that the API responds as expected. Assuming the portlet is named MyPortlet and deployed to a local servlet container on port 8080 (e.g. Tomcat), you can check the API with the following URL:
http://localhost:8080/MyPortlet/v1/api/test
This should display {"text":"testing me"}
.
If that worked, try the following:
http://localhost:8080/MyPortlet/v1/api/test?name=you
See the use of the parameter?
The default content type returned by the REST service is application/json. You can add a ".json" to the URL to get the same results.
What about ...
http://localhost:8080/MyPortlet/v1/api/test.xml
<message>
<text>testing Me</text>
</message>
Thanks to JAXB and the XML annotations, the response is now in XML!
