JAX-RS Basics
JAX-RS is a specification providing a standard way to write REST resources in Java. SeedStack implements the JAX-RS standard through the Jersey 2 implementation.
Working with JAX-RS resources
JAX-RS resources are annotated with at least a @Path
and an HTTP verb annotation:
@Path("/hello")
public class HelloResource {
@GET
@Produces("text/plain")
@Path("/{msg}")
public String sayHello(@PathParam("msg") String message) {
return "Hello " + message;
}
}
This resource is exposed by default on /hello/{msg}
. The {msg} part is a mandatory path parameter. This resource
produces simple text content (text/plain
mime-type). You can request the resource with:
curl 'http://localhost:8080/hello/world'
The returned response body is:
Hello world
You can find more information about how to declare JAX-RS resources here.
Requests
An HTTP request is mapped to resource method according to its: path, verb and content-type. If no resource method matches an HTTP request, the HTTP status 405 (method not allowed) is returned.
Path
The resource path is determined by the @Path
annotation. This annotation is mandatory
on the class and can be also added on the method to express the notion of sub-resources. The annotation value parameter
contains the relative URI path which can be an URI template or even a regular expression.
Verb
HTTP verb is determined by a corresponding annotation. Each verb has its own annotation: @GET
,
@POST
, @PUT
, @DELETE
, etc…
Content-type
Content-type negotiation and mapping is determined by the @Produces
and
@Consumes
annotations.
Responses
JAX-RS allows to return a detailed response using the Response builder methods. For instance the following resource will return HTTP status code 201 (Created) with the URI of the created resource.
@Path("/products")
public class ProductsResource {
@Inject
private CatalogService catalogService;
@GET
@Path("/{id}")
@Produces({MediaType.APPLICATION_JSON})
public ProductRepresentation getProduct(@PathParam("id") long productId) {
Optional<Product> product = catalogService.findProduct(productId);
if (!product.isPresent()) {
throw new NotFoundException("Product " + productId + " cannot be found");
}
return new ProductRepresentation(product.get());
}
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response createProduct(ProductRepresentation pr, @Context UriInfo uriInfo) {
Product newProduct = catalogService.addProduct(
pr.getDesignation(),
pr.getPrice());
URI newUri = new URI(
uriInfo.getRequestUri().toString() +
"/" +
newProduct.getId());
return Response.created(newUri).entity(newProduct).build();
}
}
Exception handling
Exception handling is an important part of any API design. Carefully designed error handling will allow you to provide meaningful status codes and messages to the client instead of returning a 500 HTTP response every time.
Web application exceptions
One way to implement clean exception handling in your REST API is to use the WebApplicationException
class, either directly or one of its predefined sub-classes or by extending the class yourself:
class:
public class ProductNotFoundException extends WebApplicationException {
public ProductNotFoundException(Product product) {
super(Response.status(Response.Status.NOT_FOUND).entity(product.getId()).build());
}
}
If the exception is thrown from within a resource method, the server will return an HTTP status code 404
.
Exception mappers
Another way of implementing exception handling is to map existing exceptions to a Response
by implementing an ExceptionMapper
and annotating it with Provider
:
@Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
@Override
public Response toResponse(MyException myException) {
return Response
.status(Response.Status.BAD_REQUEST)
.entity(myException.getSomeDetails())
.build();
}
}
If the a MyException
exception class is thrown from a JAX-RS resource, the server will return an HTTP status code 400
with custom details as the returned entity.
Working with streams
Sending a stream
To stream raw bytes (like image data) to the client, JAX-RS can return a special object:
public class MyStreamingResource {
@GET
@Produces("text/plain")
public StreamingOutput hello() {
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException, WebApplicationException {
output.write("Hello World".getBytes());
}
};
}
}
Receiving a stream
To receive a raw bytes stream (like a file upload), JAX-RS can provide a Reader or an InputStream:
@Path("/files")
public class FileResource {
@POST
@Path("/upload")
@Consumes("application/pdf")
public void doSomethingWithInputStream(InputStream inputStream) {
doSomeReading(inputStream);
}
@POST
@Path("/upload-image")
public void doSomethingWithReader(@FormDataParam("file") Reader reader) {
doSomeReading(reader);
}
}
Custom formats
SeedStack REST support works out-of-the-box with XML and JSON formats. If you requires a custom format, you can implement your own readers and/or writers:
- Create a class which implements
MessageBodyWriter
and/orMessageBodyReader
with the custom format specified as the generic type. - Add the
@Provider
annotation. - Add the
@Produces
if this is a writer. - Add the
@Consumes
if this is a reader. - Implement the necessary methods.
Details about custom formats can be found here.