-
Notifications
You must be signed in to change notification settings - Fork 1
REST Refresh Notes and Tips
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.
https://github.com/boundlessgeo/geoserver/tree/spring_mvc_example
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
The basic parts of the architecture are as follows. More information can be found in the JavaDocs for each object.
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
}
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());
}
}
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:
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);
}
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.
©2020 Open Source Geospatial Foundation