Spring MVC:用于包装API响应的适当扩展点


问题内容

我有一个简单的REST API,使用Spring MVC
@Controller和构建@RequestMapping。我想开始包装响应以提供其他元数据。

例如,给定一个将返回的呼叫

HTTP GET: /users/1
{
    "userName" : "Jack Jackerson"
}

我想包装一下,如下所示:

{
    "metadata" : 
    {
        "callLimit" : "50",
        "callsRemaining" : "49"
    },
    "result" :
    {
        "userName" : "Jack Jackerson"
    }
}  ..etc..

另外,我想支持用于管理列表(limitoffset)的标准参数集。

由于这涉及到所有api方法,因此我想将其实现为某些spring内部服务的装饰器,因此这些方法本身可以专注于其实际逻辑,并使此样板内容集中化。

我已经开始装饰装饰的HttpMessageConverter,然后用装饰器包装它们。

但是,对于没有声明的方法,这不能使我访问入站请求@RequestBody。(很多都没有)

理想情况下,我需要在堆栈中更高–
RequestResponseBodyMethodProcessor.writeWithMessageConverters()看起来不错,但我不知道该怎么做。

Spring MVC提供哪些选项来实现这种类型的请求/响应的API范围处理?


问题答案:

这是我使用的实现:

public class MetadataInjectingReturnValueHandler implements HandlerMethodReturnValueHandler {

    private final HandlerMethodReturnValueHandler delegate;

    public MetadataInjectingReturnValueHandler(HandlerMethodReturnValueHandler delegate)
    {
        this.delegate = delegate;
    }
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return delegate.supportsReturnType(returnType);
    }

    @Override
    public void handleReturnValue(Object returnValue,
            MethodParameter returnType, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
        returnValue = wrapResult(returnValue); //Omitted
        delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
}



@Component
public class MetadataInjectionFactoryBean implements InitializingBean {

    @Autowired
    private RequestMappingHandlerAdapter adapter;
    @Override
    public void afterPropertiesSet() throws Exception {
        HandlerMethodReturnValueHandlerComposite returnValueHandlers = adapter.getReturnValueHandlers();
        List<HandlerMethodReturnValueHandler> handlers = Lists.newArrayList(returnValueHandlers.getHandlers());
        decorateHandlers(handlers);
        adapter.setReturnValueHandlers(handlers);
    }
    private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        for (HandlerMethodReturnValueHandler handler : handlers) {
            if (handler instanceof RequestResponseBodyMethodProcessor)
            {
                MetadataInjectingReturnValueHandler decorator = new MetadataInjectingReturnValueHandler(handler);
                int index = handlers.indexOf(handler);
                handlers.set(index, decorator);
                log.info("Metadata Injecting decorator wired up");
                break;
            }
        }       
    }

}