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

时间:2012-11-01 08:21:52

标签: java spring spring-mvc

我有一个简单的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范围的请求/响应处理?

1 个答案:

答案 0 :(得分:6)

以下是我使用的实现:

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;
            }
        }       
    }

}