Because a lot of my current projects are using JAX-RS in different versions I’d like to write down and share some frequently used snippets for implementing RESTful web-services with the JAX-RS specification here.
Using RegEx in Path Expressions
Sometimes we need to extract multiple parameters from a path expression e.g. in the following example where year, month and day are fragments if the path.
@GET
@Path("/orders/{year:\\d{4}}-{month:\\d{2}}-{day:\\d{2}}")
@Produces(MediaType.TEXT_PLAIN)
public Response getOrders(@PathParam("year") final int year, @PathParam("month") final int month, @PathParam("day") final int day) {
return Response.ok("Year: " + year + ", month: " + month + ", day: " + day).build();
}
When we’re calling the method e.g. using curl, we’re getting the expected result:
$ curl -XGET http://localhost:8080/app/rs/orders/2004-12-24
Year: 2004, month: 12, day: 24%
Using Default Parameters
Instead of handling expected values manually we may use a simply annotation instead:
@GET
public Response getUsers(@DefaultValue("10") @QueryParam("limit") int limit){
// ...
}
Handling and Mapping Exceptions
Sometimes we want to modify exceptions being passed to the client, filter the payload, set a specific HTTP status for the response:
So first of all let’s say we have the following exception:
class CustomException extends Exception {}
Then the following provider allows us to pass the exception and set the HTTP response status to 400 (BAD_REQUEST)
@Provider
public class ConcurrentEntityModificationExceptionMapper implements ExceptionMapper<CustomException> {
@Override
public Response toResponse(final CustomException e) {
return Response.status(Status.BAD_REQUEST).entity(e).type(MediaType.APPLICATION_JSON).build();
}
}
Finally our RESTful webservice just needs to declare the exception thrown
@Path("/foo")
@Stateless
public class SomeService {
@GET
public Response getSth() throws CustomException {
// do sth.
return Response.ok().build();
}
}
Sub-Resources
Sometimes when a web-service implementation is growing too big or we want to encapsulate similar operations in a separate class, we may separate concerns using sub-resources:
This is our base web-service that’s getting the sub-resources injected and delegates the work to its sub-resources.
@Path("/permissions")
@Stateless
public class PermissionService {
@Inject
ReadPermissionService readPermissionService;
@Inject
WritePermissionService writePermissionService;
@Path("read")
public ReadPermissionService readPermissions(){
return readPermissionService;
}
@Path("write")
public WritePermissionService writePermissions(){
return writePermissionService;
}
}
This is one example of a sub-resource here:
@Stateless
public class ReadPermissionService {
@GET
@Path("{name}")
@Produces(MediaType.APPLICATION_JSON)
public Response getReadPermission(@PathParam("name") final String name) {
// do stuff
return Response.ok().build();
}
}
Setting the REST-Service’s Application Path
Say we’d like to set “/rs” as the prefix for all RESTful web-services in our application, depending on the application server and JAX-RS version used we may either set it by modifying the web.xml or using a configuration class.
Option 1: Modify web.xml
<servlet>
<servlet-name>RESTServlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>RESTServletSPI</servlet-name>
<url-pattern>/rs/*</url-pattern>
</servlet-mapping>
Option 2: Configuration class
@ApplicationPath("/rs")
public class RESTConfiguration extends Application {}
Obtain Contextual Information
Sometimes we need to access information from the HTTP request or the servlet configuration in our REST service.
JAX-RS allows us to inject instance of the following contextual objects using the qualifier @Context.
The following classes may be injected:
An example:
@GET
public Response echo(@Context HttpServletRequest req){
// do stuff with request ..
return Response.ok().build();
}
Handling Files with specific Media Types
Let’s say we have an image store and we’d like to allow the users of our service to download images of different media types by their given file-name.
This is our solution:
@GET
@Path("/imagestore/{image}")
@Produces("image/*")
public Response fetchImage(@PathParam("image") final String image) {
File imageFile = new File("/tmp/" + image);
if (!imageFile.exists()) {
throw new WebApplicationException(404);
}
String mediaType = new MimetypesFileTypeMap().getContentType(imageFile);
return Response.ok(imageFile, mediaType).build();
}
We’re now able to download an image with the correct media type using the following URL: /rs/images/head.png
Handling Generic Collections
Type erasure might cause errors when the type information in a generic collection is needed to select a suitable message body writer. That’s why we’re using the wrapper class, GenericEntity<T> here.
@GET
@Path("/foo")
public Response getUsers(){
List<User> users = userBean.findAll();
return Response.ok(new GenericEntity<List<User>>(users){}).build();
}
De-/Serialize Custom Domain Objects
Sometimes we encounter domain objects that may not be wrapped using JAX-B annotations or Jackson/YourTrustedJsonProvider annotations so that we need to write custom serializers and deserializers for them.
Here is an example for this use-case:
Custom MessageBodyWriter
@Provider
public class CarToExoticFormatWriter implements MessageBodyWriter<Car> {
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return Car.class.isAssignableFrom(type);
}
@Override
public long getSize(Car car, Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
return -1;
}
@Override
public void writeTo(Car car, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> headers, OutputStream out) throws IOException {
Writer Writer = new OutputStreamWriter(out);
// ..
writer.flush();
writer.close();
}
}
Custom MessageBodyReader
@Provider
@Consumes("mycustomformat")
public class MyCustomUnmarshaller implements MessageBodyReader {
@Override
public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public Object readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap multivaluedMap, InputStream is) throws IOException, WebApplicationException {
Object result = .... // unmarshal here
return result;
}
}
Resources
Additional REST articles of mine
Please feel free to have a look at these tutorials of mine covering different aspects of handling or creating RESTful webservices.
-
Integrating Swagger into a Spring Boot RESTful Webservice with Springfox
-
Testing RESTful Web Services made easy using the REST-assured Framework
-
REST-assured vs Jersey-Test-Framework: Testing your RESTful Web-Services
-
Creating a REST Client Step-by-Step using JAX-RS, JAX-B and Jersey
-
Creating REST Clients for JAX-RS based Webservices with Netflix Feign
Article Updates
-
2015-08-06: Links to other REST articles of mine added.
-
2015-10-22: Link list updated.