使用 Spring 的 @RequestBody 并随后读取 HttpServletRequest.getInputStream()

发布于 2024-09-30 18:29:00 字数 384 浏览 3 评论 0原文

我使用 Spring 的 @RequestBody 注释和 MappingJacksonHttpMessageConverter 将请求的 JSON POST 数据映射到一个对象中。但是之后我想读取 String 形式的数据来进行一些额外的身份验证。但是当编组发生时,HttpServletRequest 中的InputStream 为空。一旦我从方法中删除了 @RequestBody 参数,将 POST 数据读入 String 就会按预期工作。

我是否必须通过放弃 @RequestBody 并以某种方式手动进行绑定来妥协,或者是否有更优雅的解决方案?

I'm mapping my request's JSON POST data into an object using Spring's @RequestBody annotation and MappingJacksonHttpMessageConverter. However after that I'd like to read the data in String form to do some additional authentication. But when the marshalling has happened, the InputStream in HttpServletRequest is empty. Once I remove the @RequestBody parameter from the method the reading of POST data into a String works as expected.

Do I have to compromise by giving up the @RequestBody and doing the binding somehow manually or is there a more elegant solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

再浓的妆也掩不了殇 2024-10-07 18:29:00

因此,基本上您需要计算请求正文的哈希值。优雅的方法是将装饰器应用于InputStream

例如,在处理程序方法内部(在这种情况下,您不能使用 @RequestBody 并且需要手动创建 HttpMessageConverter):

@RequestMapping(...)
public void handle(HttpServletRequest request) throws IOException {
    final HashingInputStreamDecorator d = 
        new HashingInputStreamDecorator(request.getInputStream(), secretKey);
    HttpServletRequest wrapper = new HttpServletRequestWrapper(request) {
        @Override
        public ServletInputStream getInputStream() throws IOException {
            return d;
        }
    };

    HttpMessageConverter conv = ...;
    Foo requestBody = (Foo) conv.read(Foo.class, new ServletServerHttpRequest(wrapper));
    String hash = d.getHash();

    ...
}

其中在重写的 中增量计算哈希值读取HashingInputStreamDecorator 方法。

如果您创建 Filter 来应用装饰器,您还可以使用 @RequestBody。在这种情况下,装饰器可以将计算出的哈希值作为请求属性传递给处理程序方法。但是,您需要仔细映射此过滤器,以仅将其应用于对特定处理程序方法的请求。

So, basically you need to compute a hash of the request body. The elegant way to do it is to apply a decorator to the InputStream.

For example, inside a handler method (in this case you can't use @RequestBody and need to create HttpMessageConverter manually):

@RequestMapping(...)
public void handle(HttpServletRequest request) throws IOException {
    final HashingInputStreamDecorator d = 
        new HashingInputStreamDecorator(request.getInputStream(), secretKey);
    HttpServletRequest wrapper = new HttpServletRequestWrapper(request) {
        @Override
        public ServletInputStream getInputStream() throws IOException {
            return d;
        }
    };

    HttpMessageConverter conv = ...;
    Foo requestBody = (Foo) conv.read(Foo.class, new ServletServerHttpRequest(wrapper));
    String hash = d.getHash();

    ...
}

where hash is computed incrementally in overriden read methods of HashingInputStreamDecorator.

You can also use @RequestBody if you create a Filter to apply the decorator. In this case decorator can pass the computed hash to the handler method as a request attribute. However, you need to map this filter carefully to apply it only to the requests to specific handler method.

谁与争疯 2024-10-07 18:29:00

在 urlMapping bean 中,您可以声明其他拦截器列表:

  <bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
      <list>
        <bean class="org.foo.MyAuthInterceptor"/>
      </list>
    </property>
  </bean>

这些拦截器可以访问 HttpServletRequest,但如果您从流中读取数据,则参数映射器可能无法读取它。

public class AuthInterceptor extends HandlerInterceptorAdapter {

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ...
  }

  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) {
    ...
  }
}

In your urlMapping bean you can declare list of additional interceptors:

  <bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
      <list>
        <bean class="org.foo.MyAuthInterceptor"/>
      </list>
    </property>
  </bean>

Those interceptors have access to HttpServletRequest, though if you read from the stream the chances are that parameter mapper won't be able to read it.

public class AuthInterceptor extends HandlerInterceptorAdapter {

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ...
  }

  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) {
    ...
  }
}
﹏半生如梦愿梦如真 2024-10-07 18:29:00

如果我理解正确的话,JAX-RS(在绑定请求方面与 Spring MVC 有点相似)使用的一种常见方法是首先“绑定”到一些中间原始类型(通常是 byte[],但 String 也可以) ,并使用底层数据绑定器 (Jackson) 手动将其绑定到对象。我经常这样做是为了能够完全自定义数据绑定的错误处理。

If I understand this correctly, one common way used with JAX-RS (which is somewhat similar to Spring MVC with respect to binding requests) is to first "bind" into some intermediate raw type (usually byte[], but String also works), and manually bind from that to object, using underlying data binder (Jackson). I often do this to be able to fully customize error handling of data binding.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文