Java 如何使用Jersey拦截器获取请求正文

Java 如何使用Jersey拦截器获取请求正文,java,jersey,jax-rs,Java,Jersey,Jax Rs,我正在项目中使用REST球衣。所有POST数据都以JSON格式发送,并在服务器端解组到各自的bean中。大概是这样的: @Override public ContainerRequest filter(ContainerRequest req) { try{ StringWriter writer = new StringWriter(); IOUtils.copy(r

我正在项目中使用
REST球衣
。所有POST数据都以
JSON
格式发送,并在服务器端解组到各自的bean中。大概是这样的:

        @Override
        public ContainerRequest filter(ContainerRequest req) {
            try{

            StringWriter writer = new StringWriter();       
                IOUtils.copy(req.getEntityInputStream(), writer, "UTF-8");

            //This is your POST Body as String      
            String body = writer.toString();

                }
            catch (IOException e) {}
         return req; }
正在向服务器发送请求:

    $('a#sayHelloPost').click(function(event){
        event.preventDefault();
        var mangaData = {
            title:'Bleach',
            author:'Kubo Tite'
        }
        var formData=JSON.stringify(mangaData);
        console.log(formData);
        $.ajax({
                url:'rest/cred/sayposthello',
                type: 'POST',
                data: formData,
                dataType: 'json',
                contentType:'application/json'
        })
});
有效载荷:

{"title":"Bleach","author":"Kubo Tite"}
服务器端:

@POST
@Path("/sayposthello")
@Produces(MediaType.APPLICATION_JSON)
public Response sayPostHello(MangaBean mb){
    System.out.println(mb);
    return Response.status(200).build();
}
曼加贝:

public class MangaBean {
    private String title;
    private String author;
    @Override
    public String toString() {
        return "MangaBean [title=" + title + ", author=" + author + "]";
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}
控制台上的输出:

MangaBean [title=Bleach, author=Kubo Tite]
我从中获得了剩余的拦截器实现


我想访问拦截器中的有效负载(请求主体)。由于数据是JSON格式的,因此它不能作为请求参数访问有没有办法在拦截器方法中获取请求正文?请给出建议。

您可以从请求输入流中获取帖子正文。 试着这样做:

        @Override
        public ContainerRequest filter(ContainerRequest req) {
            try{

            StringWriter writer = new StringWriter();       
                IOUtils.copy(req.getEntityInputStream(), writer, "UTF-8");

            //This is your POST Body as String      
            String body = writer.toString();

                }
            catch (IOException e) {}
         return req; }

您只能读取内容一次,因为它是一个输入流。如果您在拦截器中获取它,那么您将无法在主解析中提供它

您需要做的是创建一个过滤器,该过滤器读取数据并将其提供给任何其他需要它的人。基本步骤是:

  • 在过滤器中,创建HttpServletRequestWrapper的新子类,该子类将输入读入缓冲区,并重写getInputStream()以向该缓冲区提供新的输入流
  • 将新的子类向下传递

    • 这里有一种方法可以实现,非常类似于Jersey实现其日志过滤器的方式。您可以读取实体并将其粘贴回请求,这样您就不会意外地在筛选器中使用它

      import com.sun.jersey.api.container.ContainerException;
      import com.sun.jersey.core.util.ReaderWriter;
      import com.sun.jersey.spi.container.ContainerRequest;
      import com.sun.jersey.spi.container.ContainerRequestFilter;
      
      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      
      public class JerseyFilter implements ContainerRequestFilter {
      
          @Override
          public ContainerRequest filter(ContainerRequest request) {
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              InputStream in = request.getEntityInputStream();
              final StringBuilder b = new StringBuilder();
              try {
                  if (in.available() > 0) {
                      ReaderWriter.writeTo(in, out);
      
                      byte[] requestEntity = out.toByteArray();
                      printEntity(b, requestEntity);
      
                      request.setEntityInputStream(new ByteArrayInputStream(requestEntity));
                  }
                  return request;
              } catch (IOException ex) {
                  throw new ContainerException(ex);
              }
      
          }
      
          private void printEntity(StringBuilder b, byte[] entity) throws IOException {
              if (entity.length == 0)
                  return;
              b.append(new String(entity)).append("\n");
              System.out.println("#### Intercepted Entity ####");
              System.out.println(b.toString());
          }
      }
      

      不要使用.available()中的
      方法检查Inputstream的总字节数。某些客户端未设置此值。在这种情况下,即使消息体输入流存在,也将是错误的

       InputStream in = request.getEntityInputStream();
              final StringBuilder b = new StringBuilder();
              try {
                  if (in.available() > 0)
      
      使用下面的代码

      String json = IOUtils.toString(requestContext.getEntityStream(), Charsets.UTF_8);
                  messageBody = json;
                  InputStream in = IOUtils.toInputStream(json);
                  requestContext.setEntityStream(in);
      

      我以前确实尝试过从InputStream读取,但得到了一个
      java.io.EOFException:由于输入结束,没有要映射到对象的内容。你的例子也是如此。有什么建议吗?你必须决定你想在哪里读这个东西。一旦从过滤器中读取对象(mb),Jackson将无法使用该对象,因为InputStream已被使用。原始InputStream在代码中未关闭。我将以try-with-resources的方式完成这项工作,并在try-with-resources块之外分配新构造的输入流。此外,ContainerRequestFilter接口从那时起已被修改,filter()签名现在不同了。request.setEntityInputStream(new ByteArrayInputStream(requestEntity));为什么我们需要将输入流再次设置为ContainerRequest对象?@DeepakGangore,因为一旦读取原始输入流,它将消耗请求负载,因此服务器将无法接收原始流,从而干扰请求过程