【问题标题】:How to POST a JSON object to a JAX-RS service如何将 JSON 对象发布到 JAX-RS 服务
【发布时间】:2011-10-07 23:10:09
【问题描述】:

我正在使用 JAX-RS 的 Jersey 实现。我想向该服务发布一个 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"
}

【问题讨论】:

    标签: json jersey jax-rs


    【解决方案1】:

    答案出奇地简单。我必须在POST 请求中添加一个Content-Type 标头,其值为application/json。如果没有此标头,Jersey 不知道如何处理请求正文(尽管有 @Consumes(MediaType.APPLICATION_JSON) 注释)!

    【讨论】:

    • Consumes 注释只规定了服务器端方法可接受的内容(因此它可以进一步过滤掉 HTTP 标头中没有 Content-Type: application/json 的请求)。无论方法上的消耗如何,您的请求都需要具有 Content-Type 的原因是,您使用的 JSON 库的 MessageBodyReader 可能仅在请求正文具有 Content-Type: application/json 时尝试反序列化请求正文(因为MessageBodyReader 可能有一个@Consumes(MediaType.APPLICATION_JSON)。
    • 如果有人使用 apache 的 HttpClient,下面是设置 json 字符串的方法: postMethod.setRequestEntity(new StringRequestEntity(jsonString, "application/json", null));
    • 我忘记在 POJO 顶部添加 @XmlRootElement 注释(在您的情况下为 Order 类)
    • 我遇到了和你一样的问题,而且 Content-Type:application/json 还不够。我还必须在 Spring Beans xml 中配置一个 JsonProvider: package.to.class.Order
    • 就我而言,我必须进行以下更改:
      post.setEntity(new StringEntity(projdetailpojo.toString())); post.setHeader("Content-Type", "application/json"); HttpResponse response = client.execute(post);
    【解决方案2】:

    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>
    

    【讨论】:

      【解决方案3】:

      通过 PUT/PUSH 请求向我的 JAX-rs 服务发送序列化为 JSON 的对象时,我遇到了相同的 415 http 错误,换句话说,我的服务器无法从 JSON 中反序列化对象。 在我的例子中,服务器在将它们发送到它的响应中时能够成功地序列化 JSON 中的相同对象。

      正如其他回复中提到的,我已将AcceptContent-Type 标头正确设置为application/json,但这还不够。

      解决方案

      我只是忘记了我的 DTO 对象没有参数的默认构造函数。是的,这与 @Entity 对象背后的原因相同,您需要一个不带参数的构造函数,以便 ORM 实例化对象并稍后填充字段。

      向我的 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;
          }
      }
      

      我失去了几个小时,我希望这会节省你的时间 ;-)

      【讨论】:

        猜你喜欢
        • 2011-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-22
        • 2017-05-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多