如何向 JAX-RS 服务 POST 一个 JSON 对象

77
我正在使用 JAX-RS 的 Jersey 实现。我想要向该服务 POST 一个 JSON 对象,但是我得到了一个错误代码 415 Unsupported Media Type。我错过了什么吗?
这是我的代码:
@Path("/orders")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class OrderResource {

    private static Map<Integer, Order> orders = new HashMap<Integer, Order>();

    @POST
    public void createOrder(Order order) {

        orders.put(order.id, order);
    }

    @GET
    @Path("/{id}")
    public Order getOrder(@PathParam("id") int id) {
        Order order = orders.get(id);
        if (order == null) {
            order = new Order(0, "Buy", "Unknown", 0);
        }
        return order;
    }
}

以下是 Order 对象:

public class Order {
    public int id;
    public String side;
    public String symbol;
    public int quantity;
    ...
}

像这样的GET请求完美地工作,并以JSON格式返回订单:

GET http://localhost:8080/jaxrs-oms/rest/orders/123 HTTP/1.1

但是像这样的POST请求返回415:

POST http://localhost:8080/jaxrs-oms/rest/orders HTTP/1.1

{
    "id": "123",
    "symbol": "AAPL",
    "side": "Buy",
    "quantity": "1000"
}
3个回答

87

答案出奇的简单。我需要在 POST 请求中添加一个值为 application/jsonContent-Type 头部。如果没有这个头部,在使用 @Consumes(MediaType.APPLICATION_JSON) 注解的情况下,Jersey 不知道该如何处理请求主体!


14
Consumes注解只会规定服务器端方法能够接受哪种请求(因此它会进一步过滤掉HTTP头中没有Content-Type: application/json的请求)。无论方法上面有没有Consumes注解,你的请求都需要有一个Content-Type,因为你正在使用的JSON库的MessageBodyReader可能只会尝试反序列化Content-Type为application/json的请求主体(因为MessageBodyReader可能在其上有@Consumes(MediaType.APPLICATION_JSON)注解)。 - Bryant Luk
4
如果有人正在使用Apache的HttpClient,以下是如何设置JSON字符串的方法:postMethod.setRequestEntity(new StringRequestEntity(jsonString, "application/json", null)); - Kyle
我忘记在POJO(在您的情况下是Order类)顶部添加@XmlRootElement注释。 - OneWorld
我和你遇到了完全相同的问题,Content-Type:application/json并不足够。我还需要在Spring Beans xml中配置一个JsonProvider:<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider" > <property name="singleJaxbContext" value="true" />
<property name="extraClass"> <list> <value>package.to.class.Order</value> </list> </property> </bean>
- Jérome Pieret
在我的情况下,我必须进行以下更改:<br/> post.setEntity(new StringEntity(projdetailpojo.toString())); post.setHeader("Content-Type", "application/json"); HttpResponse response = client.execute(post); - Chandru

15

Jersey使过程非常简单,我的服务类与JSON很好地配合使用,我所需要做的就是在pom.xml中添加依赖项。

@Path("/customer")
public class CustomerService {

    private static Map<Integer, Customer> customers = new HashMap<Integer, Customer>();

    @POST
    @Path("save")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public SaveResult save(Customer c) {

        customers.put(c.getId(), c);

        SaveResult sr = new SaveResult();
        sr.sucess = true;
        return sr;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("{id}")
    public Customer getCustomer(@PathParam("id") int id) {
        Customer c = customers.get(id);
        if (c == null) {
            c = new Customer();
            c.setId(id * 3);
            c.setName("unknow " + id);
        }
        return c;
    }
}

而且在pom.xml文件中

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.7</version>
</dependency>

9
当我通过PUT/PUSH请求将序列化为JSON的对象发送到我的JAX-rs服务时,遇到了同样的415 http错误,换句话说,我的服务器无法从JSON中反序列化对象。在我的情况下,当把这些对象成功地以JSON形式发送到响应中时,服务器能够正确地进行序列化。
正如其他答案中提到的那样,我已经正确设置了Accept和Content-Type标头为application/json,但这并不足够。
解决方案
我简单地忘记了DTO对象的默认构造函数。是的,这与@Entity对象背后的推理相同,您需要一个没有参数的构造函数来实例化对象并稍后填充字段。
向我的DTO对象添加没有参数的构造函数解决了我的问题。以下是类似于我的代码的示例:
错误的写法:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class NumberDTO {
    public NumberDTO(Number number) {
        this.number = number;
    }

    private Number number;

    public Number getNumber() {
        return number;
    }

    public void setNumber(Number string) {
        this.number = string;
    }
}

正确

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class NumberDTO {

    public NumberDTO() {
    }

    public NumberDTO(Number number) {
        this.number = number;
    }

    private Number number;

    public Number getNumber() {
        return number;
    }

    public void setNumber(Number string) {
        this.number = string;
    }
}

我花了很多时间,希望这篇文章能够为你节省时间;-)


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