So POSTEN Sie ein JSON-Objekt an einen JAX-RS-Dienst

73

Ich verwende die Jersey-Implementierung von JAX-RS. Ich möchte ein JSON-Objekt an diesen Dienst senden, erhalte jedoch den Fehlercode 415 Nicht unterstützter Medientyp. Was vermisse ich?

Hier ist mein Code:

@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;
    }
}

Hier ist das Order-Objekt:

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

Eine solche GET-Anfrage funktioniert einwandfrei und gibt eine Bestellung im JSON-Format zurück:

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

Eine solche POST-Anforderung gibt jedoch 415 zurück:

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

{
    "id": "123",
    "symbol": "AAPL",
    "side": "Buy",
    "quantity": "1000"
}
Naresh
quelle

Antworten:

82

Die Antwort war überraschend einfach. Ich musste Content-Typeder POSTAnfrage einen Header mit dem Wert von hinzufügen application/json. Ohne diesen Header wusste Jersey nicht, was er mit dem Anfragetext anfangen sollte (trotz der @Consumes(MediaType.APPLICATION_JSON)Anmerkung)!

Naresh
quelle
13
Die Annotation "Konsumiert" bestimmt nur, was für die serverseitige Methode akzeptabel ist (daher werden Anforderungen weiter herausgefiltert, die keinen Inhaltstyp: application / json in einem HTTP-Header enthalten). Der Grund, warum Ihre Anfrage unabhängig vom Verbrauch der Methode einen Inhaltstyp haben muss, liegt darin, dass der von Ihnen verwendete MessageBodyReader der JSON-Bibliothek wahrscheinlich nur dann versucht, den Anfragetext zu deserialisieren, wenn er einen Inhaltstyp hat: application / json (weil Der MessageBodyReader hat wahrscheinlich einen @Consumes (MediaType.APPLICATION_JSON).
Bryant Luk
4
Falls jemand den HttpClient von Apache verwendet, legen Sie die JSON-Zeichenfolge wie folgt fest: postMethod.setRequestEntity (neue StringRequestEntity (jsonString, "application / json", null));
Kyle
Ich habe vergessen, die Annotation @XmlRootElement über dem POJO (Klassenreihenfolge in Ihrem Fall) hinzuzufügen
OneWorld
Ich hatte genau das gleiche Problem wie Sie und der Inhaltstyp: application / json war nicht genug. Ich musste auch einen JsonProvider in der Spring Beans-XML konfigurieren: <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
In meinem Fall musste ich die folgenden Änderungen vornehmen: <br/>post.setEntity(new StringEntity(projdetailpojo.toString())); post.setHeader("Content-Type", "application/json"); HttpResponse response = client.execute(post);
Chandru
13

Jersey macht den Prozess sehr einfach. Meine Serviceklasse hat gut mit JSON zusammengearbeitet. Ich musste lediglich die Abhängigkeiten in die Datei pom.xml einfügen

@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;
    }
}

Und in der 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>
glm
quelle
8

415Beim Senden von in JSON serialisierten Objekten über PUT / PUSH-Anforderungen an meine JAX-rs-Dienste trat der gleiche http-Fehler auf. Mit anderen Worten, mein Server konnte die Objekte von JSON nicht de-serialisieren. In meinem Fall konnte der Server dieselben Objekte in JSON erfolgreich serialisieren, wenn er sie in seine Antworten schickte.

Wie in den anderen Antworten erwähnt, habe ich die Acceptund Content-Type-Header korrekt eingestellt application/json, aber es reicht nicht aus.

Lösung

Ich habe einfach einen Standardkonstruktor ohne Parameter für meine DTO-Objekte vergessen. Ja, dies ist die gleiche Argumentation hinter @ Entity-Objekten. Sie benötigen einen Konstruktor ohne Parameter für das ORM, um Objekte zu instanziieren und die Felder später zu füllen.

Das Hinzufügen des Konstruktors ohne Parameter zu meinen DTO-Objekten löste mein Problem. Hier folgt ein Beispiel, das meinem Code ähnelt:

Falsch

@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;
    }
}

Richtig

@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;
    }
}

Ich habe Stunden verloren, ich hoffe das rettet deine ;-)

Fabiano Tarlao
quelle