Skip to content

REST Refresh Notes and Tips

Devon Tucker edited this page Mar 23, 2017 · 1 revision

REST Notes

These are loose notes on converting the GeoServer REST API to Spring MVC. They are derived from the experience of converting the existing Styles end point. This document is meant to be a companion to the actual code.

Sample Branch

https://github.com/boundlessgeo/geoserver/tree/spring_mvc_example

Spring MVC Documentation

https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html

Architecture

The basic parts of the architecture are as follows. More information can be found in the JavaDocs for each object.

Controllers

These are the main REST request handlers. They map roughly to the existing *Resource classes in the existing rest module. For example

@RequestMapping(
    path = "/styles/{styleName}", //the path this handler is mapped to, along with the parameterized {styleName}
    method = RequestMethod.GET, //methods accepted 
    produces = { //the possible media types handled
        MediaType.APPLICATION_JSON_VALUE, 
        MediaType.APPLICATION_XML_VALUE,
        SLDHandler.MIMETYPE_10, 
        SLDHandler.MIMETYPE_11})
protected StyleInfo getStyle(
    @PathVariable String styleName) //@PathVariable indicates the arg comes from the path. Name has to match
{
    return getStyleInternal(styleName, null); //just return objects, message converter decides how to serialize
}

HttpMessageConverters

These are responsible for serialization and deserialization of response objects. These correlate to the *Format objects in the existing REST API. In most cases these just need to tie into our existing serialization (XStreamPersister).

/**
 * Message converter implementation for JSON serialization via XStream
 */
public class JSONMessageConverter extends BaseMessageConverter {

    public JSONMessageConverter(ApplicationContext applicationContext) {
        super(applicationContext);
    }
    @Override
    public boolean canRead(Class clazz, MediaType mediaType) {
        return !XStreamListWrapper.class.isAssignableFrom(clazz) &&
            MediaType.APPLICATION_JSON.equals(mediaType);
    }
    @Override
    public boolean canWrite(Class clazz, MediaType mediaType) {
        return !XStreamListWrapper.class.isAssignableFrom(clazz) &&
            MediaType.APPLICATION_JSON.equals(mediaType);
    }
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Arrays.asList(MediaType.APPLICATION_JSON);
    }
    @Override
    public Object read(Class clazz, HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException
    {
        XStreamPersister p = xpf.createJSONPersister();
        p.setCatalog(catalog);
        return p.load(inputMessage.getBody(), clazz);
    }
    @Override
    public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException {
        XStreamPersister xmlPersister = xpf.createJSONPersister();
        xmlPersister.setCatalog(catalog);
        xmlPersister.setReferenceByName(true);
        xmlPersister.setExcludeIds();
        xmlPersister.save(o, outputMessage.getBody());
    }

}

MVC configuration

MVCConfiguration is the class responsible for doing Spring MVC configuration. In particular adding converters, configuring content type negotiation, and adding intercepters. See the documentation here:

http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.html

Controller Advice

RestControllerAdvice.java is primarily used to configure error message handling, but it can also be used for a number of things. See here for more controller advice functionality.

@ExceptionHandler(RestException.class)
public void handleRestException(RestException e, HttpServletResponse response, WebRequest request, OutputStream os)
    throws IOException {
    response.setStatus(e.getStatus().value());
    StreamUtils.copy(e.getMessage(), Charset.forName("UTF-8"), os);
}

Debugging Tips

Controller not hit/Response Code 415

The most common issue I've run into during the conversion was the handler method not being hit at all. This usually results in a response code 415 from Spring (media type not accepted). Debugging this ranges from simple to aggravating. Here are a few tips, from most obvious to least:

  • Is the request path correct?
  • Does your request Content-Type match the "consumes" parameter of the handler
  • Are all your path elements matched correctly?
  • Is the HttpMessageConverter you expect to be hit -- based on the requested content type -- actually being invoked? Be sure to check the canWrite/canRead method to see that it's returning true as expected.
  • Are you requesting something via extension (say .zip) that can't actually be produced (ie. POSTING to .zip when the controller only produces XML)
  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings: This method goes through all the handlers to find the one that matches. Useful for debugging why a controller isn't being hit (415 response code). Digging around here is your last resort to find out WHY a specific handler is being rejected.
Clone this wiki locally