如何使用Jersey获取完整的REST请求体?

70

如何使用Jersey获取POST请求的完整HTTP REST请求正文?

在我们的情况下,数据将是XML格式。大小将在1K到1MB之间。

文档似乎表明您应该使用MessageBodyReader,但我找不到任何示例。

5个回答

97

事实证明,你根本不需要做太多事情。

请参见下文 - 参数x将包含完整的HTTP主体(在我们的情况下为XML)。

@POST
public Response go(String x) throws IOException {
    ...
}

7
我需要添加@Consumes(MediaType.TEXT_PLAIN)。 - Adam A
或者我猜测@Consumes可以接受任何类型的数据? - Adam A
这对于在Jersey中调试消息非常有帮助,因为出于某种原因,网络检查不是一个选项,但代码更改是(在我的情况下是本地集成测试)。 - Patrick
我遇到了以下错误:无法将类型“java.lang.String”作为元素进行编组,因为它缺少<at>XmlRootElement注释。@sdorra的答案帮助了我:只需使用org.w3c.dom.Document作为类型,而不是java.lang.String。 - koppor

20

您可以使用@Consumes注释来获取完整的请求主体:

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

@Path("doc")
public class BodyResource
{
  @POST
  @Consumes(MediaType.APPLICATION_XML)
  public void post(Document doc) throws TransformerConfigurationException, TransformerException
  {
    Transformer tf = TransformerFactory.newInstance().newTransformer();
    tf.transform(new DOMSource(doc), new StreamResult(System.out));
  }
}

注意: 发送请求时不要忘记添加"Content-Type: application/xml"头部信息。


17

试试使用这个单一的代码:

import javax.ws.rs.POST;
import javax.ws.rs.Path;

@Path("/serviceX")
public class MyClassRESTService {

    @POST
    @Path("/doSomething")   
    public void someMethod(String x) {

        System.out.println(x);
                // String x contains the body, you can process
                // it, parse it using JAXB and so on ...

    }
}

尝试 REST 服务的 URL 以... /serviceX/doSomething 结尾。


9

由于您正在使用XML传输数据,因此您也可以直接从/到POJO进行(非)编组。

Jersey用户指南中有一个示例(以及更多信息),我在此处复制:

带有JAXB注释的POJO:

@XmlRootElement
public class Planet {
    public int id;
    public String name;
    public double radius;
}

资源:

@Path("planet")
public class Resource {

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Planet getPlanet() {
        Planet p = new Planet();
        p.id = 1;
        p.name = "Earth";
        p.radius = 1.0;

        return p;
    }

    @POST
    @Consumes(MediaType.APPLICATION_XML)
    public void setPlanet(Planet p) {
        System.out.println("setPlanet " + p.name);
    }

}      
生成/消费的XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<planet>
    <id>1</id>
    <name>Earth</name>
    <radius>1.0</radius>
</planet>

0

看起来你需要在这里使用一个MessageBodyReader。以下是一个使用jdom的示例:

import org.jdom.Document;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.MediaType;
import javax.ws.rs.ext.MultivaluedMap;
import java.lang.reflect.Type;
import java.lang.annotation.Annotation;
import java.io.InputStream;

@Provider // this annotation is necessary!
@ConsumeMime("application/xml") // this is a hint to the system to only consume xml mime types
public class XMLMessageBodyReader implements MessageBodyReader<Document> {
  private SAXBuilder builder = new SAXBuilder();

  public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    // check if we're requesting a jdom Document
    return Document.class.isAssignableFrom(type);
  }

  public Document readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) {
    try {
      return builder.build(entityStream);
    }
    catch (Exception e) {
      // handle error somehow
    }
  } 
}

将这个类添加到您的Jersey部署资源列表中(通常通过web.xml配置)。然后,您可以在您的常规资源类中使用这个阅读器,就像这样:
@Path("/somepath") @POST
public void handleXMLData(Document doc) {
  // do something with the document
}

我还没有验证这是否与输入的完全相同,但这就是其要点。更多阅读内容请参见此处:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接