Skip to content

Latest commit

 

History

History
180 lines (138 loc) · 10.1 KB

guide-rest.asciidoc

File metadata and controls

180 lines (138 loc) · 10.1 KB

REST

REST (REpresentational State Transfer) is an inter-operable protocol for services that is more lightweight than SOAP. However, it is no real standard and can cause confusion (see REST philosophy). Therefore we define best practices here to guide you.

URLs

URLs are not case sensitive. Hence, we follow the best practice to use only lower-case-letters-with-hyphen-to-separate-words. For operations in REST we distinguish the following types of URLs:

  • A collection URL is build from the rest service URL by appending the name of a collection. This is typically the name of an entity. Such URL identifies the entire collection of all elements of this type. Example: https://mydomain.com/myapp/services/rest/mycomponent/v1/myentity

  • An element URL is build from a collection URL by appending an element ID. It identifies a single element (entity) within the collection. Example: https://mydomain.com/myapp/services/rest/mycomponent/v1/myentity/42

To follow KISS avoid using plural forms (…​/productmanagement/v1/products vs. …​/productmanagement/v1/product/42). Always use singular forms and avoid confusions (except for the rare cases where no singular exists).

The REST URL scheme fits perfect for CRUD operations. For business operations (processing, calculation, advanced search, etc.) we simply append a collection URL with the name of the business operation. Then we can POST the input for the business operation and get the result back. Example: https://mydomain.com/myapp/services/rest/mycomponent/v1/myentity/search

HTTP Methods

The following table defines the HTTP methods (verbs) and their meaning:

Table 1. Usage of HTTP methods
HTTP Method Meaning

GET

Read data (stateless).

PUT

Create or update data.

POST

Process data.

DELETE

Delete an entity.

Please also note that for (large) bulk deletions you may be forced to used POST instead of DELETE as according to the HTTP standard DELETE must not have payload and URLs are limited in length.

For general recommendations on HTTP methods for collection and element URLs see REST@wikipedia.

HTTP Status Codes

Further we define how to use the HTTP status codes for REST services properly. In general the 4xx codes correspond to an error on the client side and the 5xx codes to an error on the server side.

Table 2. Usage of HTTP status codes
HTTP Code Meaning Response Comment

200

OK

requested result

Result of successful GET

204

No Content

none

Result of successful POST, DELETE, or PUT with empty result (void return)

400

Bad Request

error details

The HTTP request is invalid (parse error, validation failed)

401

Unauthorized

none

Authentication failed

403

Forbidden

none

Authorization failed

404

Not found

none

Either the service URL is wrong or the requested resource does not exist

500

Server Error

error code, UUID

Internal server error occurred, in case of an exception, see REST exception handling

JAX-RS

For implementing REST services we use the JAX-RS standard. As payload encoding we recommend JSON bindings using Jackson. To implement a REST service you simply add JAX-RS annotations. Here is a simple example:

@ApplicationScoped
@Path("/imagemanagement/v1")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ImagemanagementRestService {

  @Inject
  private Imagemanagement imagemanagement;

  @GET
  @Path("/image/{id}/")
  public ImageDto getImage(@PathParam("id") long id) {

    return this.imagemanagement.findImage(id);
  }
}

Here we can see a REST service for the business component imagemanagement. The method getImage can be accessed via HTTP GET (see @GET) under the URL path imagemanagement/image/{id} (see @Path annotations) where {id} is the ID of the requested table and will be extracted from the URL and provided as parameter id to the method getImage. It will return its result (ImageDto) as JSON (see @Produces annotation - you can also extend RestService marker interface that defines these annotations for JSON). As you can see it delegates to the logic component imagemanagement that contains the actual business logic while the service itself only exposes this logic via HTTP. The REST service implementation is a regular CDI bean that can use dependency injection.

Note
With JAX-RS it is important to make sure that each service method is annotated with the proper HTTP method (@GET,@POST,etc.) to avoid unnecessary debugging. So you should take care not to forget to specify one of these annotations.

Service-Interface

You may also separate API and implementation in case you want to reuse the API for service-client:

@Path("/imagemanagement/v1")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface ImagemanagementRestService {

  @GET
  @Path("/image/{id}/")
  ImageEto getImage(@PathParam("id") long id);

}

@Named("ImagemanagementRestService")
public class ImagemanagementRestServiceImpl implements ImagemanagementRestService {

  @Override
  public ImageEto getImage(long id) {

    return this.imagemanagement.findImage(id);
  }

}

JAX-RS Configuration

Starting from CXF 3.0.0 it is possible to enable the auto-discovery of JAX-RS roots.

When the JAX-RS server is instantiated, all the scanned root and provider beans (beans annotated with javax.ws.rs.Path and javax.ws.rs.ext.Provider) are configured.

REST Exception Handling

For exceptions, a service needs to have an exception facade that catches all exceptions and handles them by writing proper log messages and mapping them to a HTTP response with an corresponding HTTP status code. For this, devon4j provides a generic solution via RestServiceExceptionFacade that you can use within your Spring applications. You need to follow the exception guide in order for it to work out of the box because the facade needs to be able to distinguish between business and technical exceptions. To implement a generic exception facade in Quarkus, follow the Quarkus exception guide.

Now your service may throw exceptions, but the facade will automatically handle them for you.

The general format for returning an error to the client is as follows:

{
  "message": "A human-readable message describing the error",
  "code": "A code identifying the concrete error",
  "uuid": "An identifier (generally the correlation id) to help identify corresponding requests in logs"
}

Pagination details

We recommend to use spring-data repositories for database access that already comes with pagination support. Therefore, when performing a search, you can include a Pageable object. Here is a JSON example for it:

{ "pageSize": 20, "pageNumber": 0, "sort": [] }

By increasing the pageNumber the client can browse and page through the hits.

As a result you will receive a Page. It is a container for your search results just like a Collection but additionally contains pagination information for the client. Here is a JSON example:

{ "totalElements": 1022,
  pageable: { "pageSize": 20, "pageNumber": 0 },
  content: [ ... ] }

The totalElements property contains the total number of hits. This can be used by the client to compute the total number of pages and render the pagination links accordingly. Via the pageable property the client gets back the Pageable properties from the search request. The actual hits for the current page are returned as array in the content property.

REST Testing

For testing REST services in general consult the testing guide.

For manual testing REST services there are browser plugins:

Security

Your services are the major entry point to your application. Hence security considerations are important here.

CSRF

A common security threat is CSRF for REST services. Therefore all REST operations that are performing modifications (PUT, POST, DELETE, etc. - all except GET) have to be secured against CSRF attacks. See CSRF how to do this.

JSON top-level arrays

OWASP earlier suggested to never return JSON arrays at the top-level, to prevent attacks without rationale. We digged deep and found anatomy-of-a-subtle-json-vulnerability. To sum it up the attack is many years old and does not work in any recent or relevant browser. Hence it is fine to use arrays as top-level result in a JSON REST service (means you can return List<Foo> in a Java JAX-RS service).