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 HandlerMethodReturnValueHandler
s 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