Ich habe eine Methode;
@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(ObjectOne objectOne, ObjectTwo objectTwo)
Jetzt weiß ich, dass ich ein einzelnes Objekt im JSON-Format veröffentlichen und es einfach in den Körper einfügen kann. Aber ist es möglich, mehrere Objekte zu erstellen? Wenn das so ist, wie?
Sie können Ihre Methode nicht wie von Tarlog korrekt angegeben verwenden.
Sie können dies jedoch tun:
@POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(List<ObjectOne> objects)
oder dieses:
@POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(BeanWithObjectOneAndObjectTwo containerObject)
Darüber hinaus können Sie Ihre Methode jederzeit mit GET-Parametern kombinieren:
@POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(List<ObjectOne> objects, @QueryParam("objectTwoId") long objectTwoId)
quelle
List<Object> objects is not working
hier Ich poste meinen Problem LinkObjectOne
, es ist keine spezielle Objektklasse, sondern ein Dummy-Name, den Sie durch Ihren eigenen Klassennamen ersetzen müssen.Wenn wir uns ansehen, was das OP versucht, versucht er / sie, zwei (möglicherweise nicht verwandte) JSON-Objekte zu veröffentlichen. Erstens ist jede Lösung, um zu versuchen, einen Teil als Körper und einen Teil als einen anderen Parameter, IMO, zu senden, schreckliche Lösungen. POST-Daten sollten in den Körper gelangen. Es ist nicht richtig, etwas zu tun, nur weil es funktioniert. Einige Workarounds verstoßen möglicherweise gegen grundlegende REST-Prinzipien.
Ich sehe ein paar Lösungen
1. Verwenden Sie application / x-www-form-urlencoded
Eine andere Möglichkeit ist, nur zu verwenden
application/x-www-form-urlencoded
. Wir können tatsächlich JSON-Werte haben. Zum Beispielcurl -v http://localhost:8080/api/model \ -d 'one={"modelOne":"helloone"}' \ -d 'two={"modelTwo":"hellotwo"}' public class ModelOne { public String modelOne; } public class ModelTwo { public String modelTwo; } @Path("model") public class ModelResource { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String post(@FormParam("one") ModelOne modelOne, @FormParam("two") ModelTwo modelTwo) { return modelOne.modelOne + ":" + modelTwo.modelTwo; } }
Das einzige, was wir brauchen, um dies zum Laufen zu bringen, ist
ParamConverterProvider
, dass dies funktioniert. Unten ist eine, die von Michal Gadjos vom Jersey Team implementiert wurde ( hier mit Erklärung ).@Provider public class JacksonJsonParamConverterProvider implements ParamConverterProvider { @Context private Providers providers; @Override public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType, final Annotation[] annotations) { // Check whether we can convert the given type with Jackson. final MessageBodyReader<T> mbr = providers.getMessageBodyReader(rawType, genericType, annotations, MediaType.APPLICATION_JSON_TYPE); if (mbr == null || !mbr.isReadable(rawType, genericType, annotations, MediaType.APPLICATION_JSON_TYPE)) { return null; } // Obtain custom ObjectMapper for special handling. final ContextResolver<ObjectMapper> contextResolver = providers .getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE); final ObjectMapper mapper = contextResolver != null ? contextResolver.getContext(rawType) : new ObjectMapper(); // Create ParamConverter. return new ParamConverter<T>() { @Override public T fromString(final String value) { try { return mapper.reader(rawType).readValue(value); } catch (IOException e) { throw new ProcessingException(e); } } @Override public String toString(final T value) { try { return mapper.writer().writeValueAsString(value); } catch (JsonProcessingException e) { throw new ProcessingException(e); } } }; } }
Wenn Sie nicht nach Ressourcen und Anbietern suchen, registrieren Sie einfach diesen Anbieter. Das obige Beispiel sollte funktionieren.
2. Verwenden Sie Multipart
Eine Lösung, die niemand erwähnt hat, ist die Verwendung von mehrteiligen . Dies ermöglicht es uns, beliebige Teile in einer Anfrage zu senden. Da jede Anforderung nur einen Entitätstext haben kann, ist Multipart die Problemumgehung, da verschiedene Teile (mit eigenen Inhaltstypen) als Teil des Entitätstexts verwendet werden können.
Hier ist ein Beispiel mit Jersey (siehe offizielles Dokument hier )
Abhängigkeit
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-multipart</artifactId> <version>${jersey-2.x.version}</version> </dependency>
Registrieren Sie die
MultipartFeature
import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; @ApplicationPath("/api") public class JerseyApplication extends ResourceConfig { public JerseyApplication() { packages("stackoverflow.jersey"); register(MultiPartFeature.class); } }
Ressourcenklasse
import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.jersey.media.multipart.FormDataParam; @Path("foobar") public class MultipartResource { @POST @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postFooBar(@FormDataParam("foo") Foo foo, @FormDataParam("bar") Bar bar) { String response = foo.foo + "; " + bar.bar; return Response.ok(response).build(); } public static class Foo { public String foo; } public static class Bar { public String bar; } }
Nun ist der schwierige Teil bei einigen Kunden, dass es keine Möglichkeit gibt, die
Content-Type
einzelnen Körperteile festzulegen, die erforderlich sind, damit die oben genannten Funktionen funktionieren. Der mehrteilige Anbieter sucht nach dem Nachrichtentextleser, basierend auf dem Typ jedes Teils. Wenn es nicht aufapplication/json
oder einen Typ eingestellt ist, für denFoo
oderBar
ein Reader vorhanden ist, schlägt dies fehl. Wir werden hier JSON verwenden. Es gibt keine zusätzliche Konfiguration, als einen Reader zur Verfügung zu haben. Ich werde Jackson benutzen. Mit der folgenden Abhängigkeit sollte keine andere Konfiguration erforderlich sein, da der Anbieter durch das Scannen von Klassenpfaden erkannt wird.<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>${jersey-2.x.version}</version> </dependency>
Nun der Test. Ich werde cURL verwenden . Sie sehen, ich setze das explizit
Content-Type
für jedes Teil mittype
. Das-F
bedeutet zu einem anderen Teil. (Eine Vorstellung davon, wie der Anfragetext tatsächlich aussieht, finden Sie ganz unten im Beitrag.)Das Ergebnis ist genau wie wir erwartet hatten. Wenn Sie sich die Ressourcenmethode ansehen, geben wir nur diese Zeichenfolge zurück
foo.foo + "; " + bar.bar
, die aus den beiden JSON-Objekten stammt.Einige Beispiele für verschiedene JAX-RS-Clients finden Sie unter den folgenden Links. Sie sehen auch einige serverseitige Beispiele aus diesen verschiedenen JAX-RS-Implementierungen. Jeder Link sollte irgendwo einen Link zur offiziellen Dokumentation für diese Implementierung enthalten
Es gibt andere JAX-RS-Implementierungen, aber Sie müssen die Dokumentation dafür selbst finden. Die oben genannten drei sind die einzigen, mit denen ich Erfahrung habe.
Was Javascript-Clients betrifft, sehe ich die meisten Beispiele (z. B. einige davon beinhalten das Setzen
Content-Type
von undefined / false (usingFormData
), damit der Browser damit umgehen kann. Dies funktioniert jedoch nicht für uns, da der Browser das nicht setztContent-Type
für jedes Teil. Und der Standardtyp isttext/plain
.Ich bin mir sicher, dass es Bibliotheken gibt, in denen der Typ für jedes Teil festgelegt werden kann. Um Ihnen jedoch zu zeigen, wie dies manuell durchgeführt werden kann, werde ich ein Beispiel veröffentlichen (von hier aus ein wenig Hilfe erhalten . Ich werde Angular verwenden , aber die grunzende Arbeit beim Aufbau des Entitätskörpers wird einfaches altes Javascript sein.
<!DOCTYPE html> <html ng-app="multipartApp"> <head> <script src="js/libs/angular.js/angular.js"></script> <script> angular.module("multipartApp", []) .controller("defaultCtrl", function($scope, $http) { $scope.sendData = function() { var foo = JSON.stringify({foo: "FooFoo"}); var bar = JSON.stringify({bar: "BarBar"}); var boundary = Math.random().toString().substr(2); var header = "multipart/form-data; charset=utf-8; boundary=" + boundary; $http({ url: "/api/foobar", headers: { "Content-Type": header }, data: createRequest(foo, bar, boundary), method: "POST" }).then(function(response) { $scope.result = response.data; }); }; function createRequest(foo, bar, boundary) { var multipart = ""; multipart += "--" + boundary + "\r\nContent-Disposition: form-data; name=foo" + "\r\nContent-type: application/json" + "\r\n\r\n" + foo + "\r\n"; multipart += "--" + boundary + "\r\nContent-Disposition: form-data; name=bar" + "\r\nContent-type: application/json" + "\r\n\r\n" + bar + "\r\n"; multipart += "--" + boundary + "--\r\n"; return multipart; } }); </script> </head> <body> <div ng-controller="defaultCtrl"> <button ng-click="sendData()">Send</button> <p>{{result}}</p> </div> </body> </html>
Der interessante Teil ist die
createRequest
Funktion. Hier erstellen wir das Multipart, setzen dasContent-Type
von jedem Teil aufapplication/json
und verketten das Stringifiedfoo
und diebar
Objekte zu jedem Teil. Wenn Sie mit dem mehrteiligen Format nicht vertraut sind, finden Sie hier weitere Informationen . Der andere interessante Teil ist der Header. Wir setzen es aufmultipart/form-data
.Unten ist das Ergebnis. In Angular habe ich das Ergebnis nur verwendet, um es im HTML-Code mit anzuzeigen
$scope.result = response.data
. Die Schaltfläche, die Sie sehen, diente nur dazu, die Anfrage zu stellen. Sie sehen auch die Anforderungsdaten in Firebug3. Wickeln Sie sie einfach in ein einzelnes übergeordnetes Objekt
Diese Option sollte selbsterklärend sein, wie andere bereits erwähnt haben.
quelle
Der nächste Ansatz wird normalerweise in solchen Fällen angewendet:
TransferObject { ObjectOne objectOne; ObjectTwo objectTwo; //getters/setters } @POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(TransferObject object){ // object.getObejctOne().... }
quelle
Sie können nicht zwei separate Objekte in einen einzelnen POST-Aufruf einfügen, wie von Tarlog erläutert.
Auf jeden Fall können Sie ein drittes Containerobjekt erstellen, das die ersten beiden Objekte enthält, und dieses innerhalb des POS-Aufrufs übergeben.
quelle
Ich habe mich auch mit diesem Problem konfrontiert. Vielleicht hilft das.
@POST @Path("/{par}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Object centralService(@PathParam("par") String operation, Object requestEntity) throws JSONException { ObjectMapper objectMapper=new ObjectMapper(); Cars cars = new Cars(); Seller seller = new Seller(); String someThingElse; HashMap<String, Object> mapper = new HashMap<>(); //Diamond ))) mapper = (HashMap<String, Object>) requestEntity; cars=objectMapper.convertValue(mapper.get("cars"), Cars.class); seller=objectMapper.convertValue(mapper.get("seller"), Seller.class); someThingElse=objectMapper.convertValue(mapper.get("someThingElse"), String.class); System.out.println("Cars Data "+cars.toString()); System.out.println("Sellers Data "+seller.toString()); System.out.println("SomeThingElse "+someThingElse); if (operation.equals("search")) { System.out.println("Searching"); } else if (operation.equals("insertNewData")) { System.out.println("Inserting New Data"); } else if (operation.equals("buyCar")) { System.out.println("Buying new Car"); } JSONObject json=new JSONObject(); json.put("result","Works Fine!!!"); return json.toString(); } *******CARS POJO********@XmlRootElement for Mapping Object to XML or JSON*** @XmlRootElement public class Cars { private int id; private String brand; private String model; private String body_type; private String fuel; private String engine_volume; private String horsepower; private String transmission; private String drive; private String status; private String mileage; private String price; private String description; private String picture; private String fk_seller_oid; } // Setters and Getters Omitted *******SELLER POJO********@XmlRootElement for Mapping Object to XML or JSON*** @XmlRootElement public class Seller { private int id; private String name; private String surname; private String phone; private String email; private String country; private String city; private String paste_date; }//Setters and Getters omitted too *********************FRONT END Looks Like This****************** $(function(){ $('#post').on('click',function(){ console.log('Begins'); $.ajax({ type:'POST', url: '/ENGINE/cars/test', contentType: "application/json; charset=utf-8", dataType: "json", data:complexObject(), success: function(data){ console.log('Sended and returned'+JSON.stringify(data)); }, error: function(err){ console.log('Error'); console.log("AJAX error in request: " + JSON.stringify(err, null, 2)); } }); //-- END of Ajax console.log('Ends POST'); console.log(formToJSON()); }); // -- END of click function POST function complexObject(){ return JSON.stringify({ "cars":{"id":"1234","brand":"Mercedes","model":"S class","body_type":"Sedan","fuel":"Benzoline","engine_volume":"6.5", "horsepower":"1600","transmission":"Automat","drive":"Full PLag","status":"new","mileage":"7.00","price":"15000", "description":"new car and very nice car","picture":"mercedes.jpg","fk_seller_oid":"1234444"}, "seller":{ "id":"234","name":"Djonotan","surname":"Klinton","phone":"+994707702747","email":"[email protected]", "country":"Azeribaijan","city":"Baku","paste_date":"20150327"}, "someThingElse":"String type of element" }); } //-- END of Complex Object });// -- END of JQuery - Ajax
quelle
Dies kann erreicht werden, indem die POST-Methode deklariert wird, um ein Array von Objekten zu akzeptieren. Beispiel wie dieses
T[] create(@RequestBody T[] objects) { for( T object : objects ) { service.create(object); } }
quelle
Ändern Sie @Consumes (MediaType.APPLICATION_JSON) in @Consumes ({MediaType.APPLICATION_FORM_URLENCODED}). Anschließend können Sie mehrere Objekte in den Body einfügen
quelle