实现RESTful API时,我将所有数据都包装在一个对象中,看起来就像这样。
{error: null, code: 200, data: {...actual 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
}
所以问题是如何修改它(可能使用自定义HttpMessageConverter可能还有其他一些方法?)只返回booksDao.readBooks()并自动包装它。
答案 0 :(得分:9)
与@Ralph建议一样,您可以使用HandlerMethodReturnValueHandler
来包装处理程序的返回值。
实现这一目标的最简单方法是扩展RequestResponseBodyMethodProcessor
并稍微改变它的行为。最好的方法是创建一个自定义注释来标记处理程序方法。这样可以确保默认情况下会调用HandlerMethodReturnValueHandler
代替RequestMappingHandlerAdapter
所包含的其他内容。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResultResponseBody {}
以下是名为HandlerMethodReturnValueHandler
的自定义ResultResponseHandlerMethodProcessor
的简单实现,它将支持从使用ResultResponseBody
注释的方法返回的值。这很简单。只需覆盖supportsReturnType()
和handleReturnValue()
方法以满足您的需求(将返回值包装为Result
类型)。
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);
}
}
唯一剩下的就是将此类添加到自定义HandlerMethodReturnValueHandler
列表中,并为其提供MappingJackson2HttpMessageConverter
实例。
@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));
}
}
答案 1 :(得分:2)
我认为,不是改变消息转换器(可以工作),而是使用AOP方法 - 对所有相关控制器方法的建议都很容易设置。它还可以为您提供更好的编程模型,以及对截获哪些方法的更细粒度的控制。
答案 2 :(得分:2)
您可以使用HandlerMethodReturnValueHandler
替换结果。
关键点是:在将(修改的)retunr值委托给序列化之前替换返回值。
有关如何归档类似(不同)目标的示例,请参阅此博客:http://martypitt.wordpress.com/2012/11/05/custom-json-views-with-spring-mvc-and-jackson/。它还描述了一种注册HandlerMethodReturnValueHandler
(另一种见Bart´s answer)
答案 3 :(得分:2)
我想试着说服你,你所做的是对的,不需要任何改变。
正如您在问题的评论中发布的那样,您有许多不同的Result
方法来设置错误消息,代码和数据。像
Result.ok(data)
Result.forbidden()
Result.badRequest("<Something> caused a syntax error.")
Result.notModified("The entity was not modified.")
我假设这些方法旨在映射到各种HTTP status codes,但带有自定义错误消息。
您的@Controller
处理程序方法用于处理请求并准备响应。这就是您的方法目前正在做什么,它非常清楚它的作用。关于Result
应该属于处理程序方法的逻辑,而不是像HandlerMethodReturnValueHandler
那样的逻辑正如其他人所提出的那样。
我甚至建议使用ResponseEntity
代替@ResponseBody
。您可以返回ResponseEntity
并明确设置HTTP响应标头和状态代码。您还可以设置响应正文。
像
这样的东西return new ResponseEntity<>(Result.ok(books));
在这种情况下,默认状态代码为200。
但是如果你想使用
return Result.forbidden();
你会用
return new ResponseEntity<>(Result.forbidden(), HttpStatus.FORBIDDEN);
Spring会使用相同的HttpMessageConverter
将您的Result
转换为JSON,但在这里您可以更好地控制HTTP响应。
答案 4 :(得分:0)
我认为有必要
将您的所有者替换为默认的RequestResponseBodyMethodProcessor 处理器
,否则默认的RequestResponseBodyMethodProcessor将控制处理返回值。