When implementing RESTful API I wrap all my data in an object so it looks like this.
{error: null, code: 200, data: {...actual data...}}
This results in repetitive code I use everywhere to wrap data:
@Transactional
@RequestMapping(value = "/", method = RequestMethod.GET)
public @ResponseBody Result<List<BookShortDTO>> books() {
List<Book> books = booksDao.readBooks();
return Result.ok(books); // this gets repeated everywhere
}
So the question is how do I modify this (maybe with use of custom HttpMessageConverter maybe some other ways?) to just return booksDao.readBooks() and to get it wrapped automatically.
Like @Ralph suggested you can use a HandlerMethodReturnValueHandler to wrap your handlers return value.
The easiest way to achieve this is by extending RequestResponseBodyMethodProcessor and alter it's behavior a bit. Best is to create a custom annotation to mark your handler methods with. This will make sure your HandlerMethodReturnValueHandler will be called instead of others included by RequestMappingHandlerAdapter by default.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResultResponseBody {}
Here is a simple implementation of the custom HandlerMethodReturnValueHandler named ResultResponseHandlerMethodProcessor which will support values returned from methods annotated with ResultResponseBody. It's pretty simple. Just override the supportsReturnType() and handleReturnValue() methods to suit your needs (wrap the return value into a Result type).
public class ResultResponseHandlerMethodProcessor extends RequestResponseBodyMethodProcessor {
public ResultResponseHandlerMethodProcessor(final List<HttpMessageConverter<?>> messageConverters) {
super(messageConverters);
}
public ResultResponseHandlerMethodProcessor(final List<HttpMessageConverter<?>> messageConverters, final ContentNegotiationManager contentNegotiationManager) {
super(messageConverters, contentNegotiationManager);
}
@Override
public boolean supportsReturnType(final MethodParameter returnType) {
return returnType.getMethodAnnotation(ResultResponseBody.class) != null;
}
@Override
public void handleReturnValue(final Object returnValue, final MethodParameter returnType, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
super.handleReturnValue(Result.ok(returnValue), returnType, mavContainer, webRequest);
}
}
The only thing left is to add this class to the list of custom HandlerMethodReturnValueHandlers and provide it with a MappingJackson2HttpMessageConverter instance.
@EnableWebMvc
@Configuration
public class ApplicationConfiguration extends WebMvcConfigurerAdapter
@Override
public void addReturnValueHandlers(final List<HandlerMethodReturnValueHandler> returnValueHandlers) {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
returnValueHandlers.add(new ResultResponseHandlerMethodProcessor(messageConverters));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With