{"id":3933,"date":"2017-07-15T12:56:20","date_gmt":"2017-07-15T10:56:20","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=3933"},"modified":"2017-07-15T12:56:20","modified_gmt":"2017-07-15T10:56:20","slug":"jax-rs-mit-jersey-teil-2-restful-web-services","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2017\/07\/jax-rs-mit-jersey-teil-2-restful-web-services\/","title":{"rendered":"JAX-RS mit Jersey, Teil 2 (RESTful Web-Services)"},"content":{"rendered":"<h3>Content-Handler, Marshaller und verschiedene MIME-Typen<\/h3>\n<p>JAX-RS erlaubt grunds\u00e4tzlich alle MIME-Typen, und die Daten selbst k\u00f6nnen auf verschiedene Java-Datentypen \u00fcbertragen werden. So ist es egal, ob bei Textdokumenten zum Beispiel der R\u00fcckgabetyp String oder OutputStream ist; selbst ein File-Objekt l\u00e4sst sich zur\u00fcckgeben. F\u00fcr einen Parametertyp \u2013 Parameter werden gleich vorgestellt \u2013 gilt das Gleiche: JAX-RS ist hier recht flexibel und kann \u00fcber einen InputStream oder Writer einen String entgegennehmen. (Reicht das nicht, k\u00f6nnen so genannte Provider angemeldet werden.)<\/p>\n<p>Bei XML-Dokumenten kommt hinzu, dass JAX-RS wunderbar mit JAXB zusammenspielt.<\/p>\n<h4>XML mit JAXB<\/h4>\n<p>Dazu ein Beispiel f\u00fcr einen Dienst hinter der URL http:\/\/localhost:8080\/api\/dating\/serverinfo, der eine Serverinformation im XML-Format liefert. Das XML wird automatisch von JAXB generiert.<\/p>\n<pre>@GET\n @Path(\u00a0\"serverinfo\"\u00a0)\n @Produces(\u00a0MediaType.TEXT_XML\u00a0)\n public\u00a0ServerInfo\u00a0serverinfo()\u00a0{\n \u00a0\u00a0ServerInfo\u00a0info\u00a0=\u00a0new\u00a0ServerInfo();\n \u00a0\u00a0info.server\u00a0=\u00a0System.getProperty(\u00a0\"os.name\"\u00a0)+\"\u00a0\"+System.getProperty(\u00a0\"os.version\"\u00a0);\n \u00a0\u00a0return\u00a0info;\n }\n\n@XmlRootElement\n class\u00a0ServerInfo {\n \u00a0\u00a0public\u00a0String\u00a0server;\n }<\/pre>\n<p>Die Klasse ServerInfo ist eine JAXB-annotierte Klasse. In der eigenen JAX-RS-Methode serverinfo() wird dieses ServerInfo-Objekt aufgebaut, das Attribut gesetzt und dann zur\u00fcckgegeben; der R\u00fcckgabetyp ist also nicht String wie vorher, sondern explizit ServerInfo. Dass der MIME-Typ XML ist, sagt @Produces(MediaType.TEXT_XML). Und noch eine Annotation nutzt das Beispiel: @Path. Lokal an der Methode bedeutet es, dass der angegebene Pfad zus\u00e4tzlich zur Pfadangabe an der Klasse gilt. Also ergibt sich der komplette Pfad aus: Basispfad + &#8222;dating&#8220; + &#8222;\/&#8220; + &#8222;serverinfo&#8220;. Wir k\u00f6nnen http:\/\/localhost:8080\/api\/dating\/serverinfo im Browser eingeben.<\/p>\n<h4>JSON-Serialisierung *<\/h4>\n<p>Ist der Client eines REST-Aufrufs ein JavaScript-Programm in einem Webbrowser, ist es in der Regel praktischer, statt XML das Datenformat JSON zu verwenden. JAX-RS bindet drei M\u00f6glichkeiten zum Senden von JSON:<\/p>\n<ol>\n<li>JSON-\u00dcbersetzungdes Java-Objekts \u00fcber die JSON-Bibliothek (\u201ePOJO based JSON binding support\u201c). Es lassen sich so ziemlich alle Java-Objekte abbilden.<\/li>\n<li>JAXB-basierte JSON-\u00dcbersetz Die JSON-Bibliothek liest die JAXB-annotierten Objekte aus und f\u00fchrt anhand der Metadaten die Umsetzung durch. JAXB wird hier nicht nur f\u00fcr XML verwendet, sondern auch f\u00fcr JSON; nat\u00fcrlich ergeben nicht alle Eigenschaften einen Sinn.<\/li>\n<li>Automatisch ohne ein Mapping arbeitet eine Low-Level-API. Sie gibt maximale Flexibilit\u00e4t, aber erfordert viel Handarbeit. Seit Java EE 7 ist die API im Standard, aber nicht Teil der Java SE.<\/li>\n<\/ol>\n<p>Schauen wir uns die zweite L\u00f6sung an. Jersey unterst\u00fctzt von Haus aus Jackson, MOXy und Jettison als JSON-Objekt-Mapper. Um MOXy einzusetzen m\u00fcssen weitere Java-Archive in den Klassenpfad aufgenommen werden. Wir k\u00f6nnen die Abh\u00e4ngigkeiten \u00fcber Maven beschreiben, dann ist folgendes in der pom.xml aufzunehmen:<\/p>\n<pre>&lt;dependency&gt;\n\n&lt;groupId&gt;org.glassfish.jersey.media&lt;\/groupId&gt;\n\n&lt;artifactId&gt;jersey-media-moxy&lt;\/artifactId&gt;\n\n&lt;version&gt;2.25&lt;\/version&gt;\n\n&lt;\/dependency&gt;<\/pre>\n<p>Oder zu Fu\u00df m\u00fcssen die folgenden JAR-Dateien geladen und dann im Klassenpfad aufgenommen werden:<\/p>\n<ul>\n<li>https:\/\/mvnrepository.com\/artifact\/org.glassfish.jersey.media\/jersey-media-moxy\/<\/li>\n<li>https:\/\/mvnrepository.com\/artifact\/org.glassfish.jersey.ext\/jersey-entity-filtering\/<\/li>\n<li>https:\/\/mvnrepository.com\/artifact\/org.eclipse.persistence\/org.eclipse.persistence.moxy\/<\/li>\n<li>https:\/\/mvnrepository.com\/artifact\/org.eclipse.persistence\/org.eclipse.persistence.core\/<\/li>\n<li>https:\/\/mvnrepository.com\/artifact\/org.glassfish\/javax.json\/\n<ul>\n<li>Um von XML auf JSON zu wechseln muss bei den REST-Methoden lediglich der APPLICATION_JSON eingesetzt werden:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre>@GET\n\n@Path( \"jsonserverinfo\" )\n\n<strong>@Produces( MediaType.APPLICATION_JSON )<\/strong>\n\npublic ServerInfo jsonserverinfo() {\n\n\u00a0 return serverinfo();\n\n}<\/pre>\n<p>Das reicht schon aus, und der Server sendet ein JSON-serialisiertes Objekt.<\/p>\n<h4>Alles Verhandlungssache<\/h4>\n<p>Die JAX-RS-API bietet mit dem MIME-Typ noch eine Besonderheit, dass der Server unterschiedliche Formate liefern kann, je nachdem, was der Client verarbeiten m\u00f6chte oder kann. Der Server macht das mit @Produces klar, denn dort kann eine Liste von MIME-Typen stehen. Soll der Server XML und JSON generieren k\u00f6nnen, schreiben wir:<\/p>\n<pre>@GET\n\n@Path( \"jsonxmlserverinfo\" )\n\n@Produces( <strong>{ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }<\/strong> )\n\npublic ServerInfo jsonxmlserverinfo() {\n\n\u00a0 return serverinfo();\n\n}<\/pre>\n<p>Kommt der Client mit dem Wunsch nach XML, bekommt er XML, m\u00f6chte er JSON, bekommt er JSON. Die Jersey-Client-API teilt \u00fcber request(String) bzw. request(MediaType\u2026 types) mit, was ihr Wunschtyp ist. (Dieser Typwunsch ist eine Eigenschaft von HTTP und nennt sich Content Negotiation.)<\/p>\n<pre>WebTarget wr1 = ClientBuilder.newClient().target( \"http:\/\/localhost:8080\/api\" );\n\nBuilder b1 = wr1.path( \"dating\" ).path( \"jsonxmlserverinfo\" )\n\n\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.request( <strong>MediaType.APPLICATION_JSON<\/strong> );\n\nSystem.out.println( b1.get( ServerInfo.class ).getServer() );\u00a0 \/\/ Windows 10 10.0\n\n\n\n\nWebTarget wr2 = ClientBuilder.newClient().target( \"http:\/\/localhost:8080\/api\" );\n\nBuilder b2 = wr2.path( \"dating\" ).path( \"jsonxmlserverinfo\" )\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .request( <strong>MediaType.TEXT_XML<\/strong> );\n\nSystem.out.println( b2.get( ServerInfo.class ).getServer() );\u00a0 \/\/ Windows 10 10.0\n\n\n\n\nWebTarget wr3 = ClientBuilder.newClient().target( \"http:\/\/localhost:8080\/api\" );\n\nBuilder b3 = wr3.path( \"dating\" ).path( \"jsonxmlserverinfo\" )\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0.request( <strong>MediaType.TEXT_PLAIN<\/strong> );\n\ntry {\n\n\u00a0 System.out.println( b3.get( ServerInfo.class ).getServer() );\n\n}\n\ncatch ( Exception e ) {\n\n\u00a0 System.out.println( e );\n\n\u00a0 \/\/ javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable\n\n}<\/pre>\n<p>Passt die Anfrage auf den Typ von @Produces, ist alles prima, ohne \u00dcbereinstimmung gibt es einen Fehler. Bei der letzten Zeile gibt es eine Ausnahme (\u201ejavax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable\u201c), da JSON und XML eben nicht purer Text sind.<\/p>\n<h3>REST-Parameter<\/h3>\n<p>Im Abschnitt \u201eWie sieht ein REST-URI aus?\u201c in Abschnitt 15.2.1 wurde ein Beispiel vorgestellt, wie Pfadangaben aussehen, wenn sie einen RESTful Service bilden:<\/p>\n<p>http:\/\/www.tutego.de\/blog\/javainsel\/category\/java-7\/page\/2\/<\/p>\n<p>Als Schl\u00fcssel-Wert-Paare lassen sich festhalten: category=java-7 und page=2. Der Server wird die URL auseinanderpfl\u00fccken und genau die Blog-Eintr\u00e4ge liefern, die zur Kategorie \u201ejava-7\u201c geh\u00f6ren und sich auf der zweiten Seite befinden.<\/p>\n<p>Bisher sah unser REST-Service auf dem Endpunkt \/api\/dating\/ so aus, dass einfach ein String zur\u00fcckgegeben wird. \u00dcblicherweise gibt es aber unterschiedliche URLs, die Operationen wie \u201efinde alle\u201c oder \u201efinde alle mit der Einschr\u00e4nkung X\u201c abbilden. Bei unseren Dating-Dienst wollen wir dem Client drei Varianten zur Abfrage anbieten (mit Beispiel):<\/p>\n<ul>\n<li>\/api\/dating\/: alle Nutzer<\/li>\n<li>\/api\/dating\/gender\/nasi: alle Nutzer mit dem Geschlecht \u201enasi\u201c<\/li>\n<li>\/api\/dating\/gender\/nasi\/age\/18-28: alle \u201enasischen\u201c-Nutzer im Alter von 18 bis 28<\/li>\n<\/ul>\n<p>Das erste Beispiel macht deutlich, dass hier ohne explizite Angabe weiterer Einschr\u00e4nkungskriterien alle Nachrichten erfragt werden sollen, w\u00e4hrend mit zunehmend l\u00e4ngerer URL weitere Einschr\u00e4nkungen dazukommen.<\/p>\n<h4>Parameter in JAX-RS kennzeichnen<\/h4>\n<p>Die JAX-RS-API erlaubt es, dass Parameter (wie eine ID oder ein Such-String) leicht eingefangen werden k\u00f6nnen. F\u00fcr die drei m\u00f6glichen URLs entstehen zum Beispiel drei \u00fcberladene Methoden:<\/p>\n<pre>@GET\u00a0@Produces(\u00a0MediaType.TEXT_PLAIN\u00a0)\n public\u00a0String\u00a0meet()\u00a0...\n \n\n\n@GET\n\n<strong>@Path( \"gender\/{gender}\" )<\/strong>\n\n@Produces( MediaType.TEXT_PLAIN )\n\npublic String meet( <strong>@PathParam( \"gender\" ) String gender<\/strong> ) {\n\n\u00a0 return String.format( \"Geschlecht = %s\", gender );\n\n}\n\n\n\n\n@GET\n\n@Produces( MediaType.TEXT_PLAIN )\n\n<strong>@Path( \"gender\/{gender}\/age\/{age}\" )<\/strong>\n\npublic String meet( <strong>@PathParam( \"gender\" ) String gender,<\/strong>\n\n<strong>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @PathParam( \"age\" ) String age<\/strong> ) {\n\n\u00a0 return String.format( \"Geschlecht = %s, Altersbereich = %s\", gender, age );\n\n}<\/pre>\n<p>Die bekannte @Path-Annotation enth\u00e4lt nicht einfach nur einen statischen Pfad, sondern beliebig viele Platzhalter in geschweiften Klammern. Der Name des Platzhalters taucht in der Methode wieder auf, n\u00e4mlich dann, wenn er mit @PathParam an einen Parameter gebunden wird. Jersey parst f\u00fcr uns die URL und f\u00fcllt die Parametervariablen passend auf bzw. ruft die richtige Methode auf. Da die JAX-RS-Implementierung den Wert f\u00fcllt, nennt sich das auch JAX-RS-Injizierung.<\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"398\">URL-Endung<\/td>\n<td width=\"366\">Aufgerufene Methode<\/td>\n<\/tr>\n<tr>\n<td width=\"398\">\/api\/dating\/<\/td>\n<td width=\"366\">meet()<\/td>\n<\/tr>\n<tr>\n<td width=\"398\">\/api\/dating\/gender\/<strong>nasi<\/strong><\/td>\n<td width=\"366\">meet( String gender )<\/td>\n<\/tr>\n<tr>\n<td width=\"398\">\/api\/dating\/gender\/<strong>nasi<\/strong>\/age\/<strong>18-28<\/strong><\/td>\n<td width=\"366\">meet( String gender, String age )<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Welche URL zu welcher Methode f\u00fchrt<\/p>\n<p>Die Implementierungen der Methoden w\u00fcrden jetzt an einen Daten-Service gehen und die selektierten Datens\u00e4tze zur\u00fcckgeben. Das zeigt das Beispiel nicht, da dies eine andere Baustelle ist.<\/p>\n<p><strong>Tipp:<\/strong>\u00a0Wenn Parameter falsch sind, kann eine Methode eine besondere Ausnahme vom Typ javax.ws.rs.WebApplicationException (dies ist eine RuntimeException) erzeugen. Im Konstruktor von javax.ws.rs.WebApplicationException l\u00e4sst sich ein Statuscode als int oder als Aufz\u00e4hlung vom Typ Response.Status \u00fcbergeben, etwa new WebApplicationException(Response.Status. EXPECTATION_FAILED).<\/p>\n<h3>REST-Services mit Parametern \u00fcber die Jersey-Client-API aufrufen<\/h3>\n<p>Wenn die URLs in dem Format schl\u00fcssel1\/wert1\/schl\u00fcssel2\/wert2 aufgebaut sind, dann ist ein Aufbau einfach durch Kaskadierung der path(\u2026)-Methoden umzusetzen:<\/p>\n<pre>System.out.println( ClientBuilder.newClient().target( \"http:\/\/localhost:8080\/api\" )\n\n\u00a0 .path( \"dating\" ).<strong>path( \"gender\" ).path( \"nasi\" )<\/strong>\n\n\u00a0 .request().get( String.class ) ); \/\/ Geschlecht = nasi\n\n\n\n\nSystem.out.println( ClientBuilder.newClient().target( \"http:\/\/localhost:8080\/api\/dating\" )\n\n\u00a0 <strong>.path( \"gender\" ).path( \"nasi\" ).path( \"age\" ).path( \"18-28\" )<\/strong>\n\n\u00a0 .request().get( String.class ) ); \/\/ Geschlecht = nasi, Altersbereich = 18-28<\/pre>\n<h4>Multiwerte<\/h4>\n<p>Schl\u00fcssel-Wert-Paare lassen sich auch auf anderen Wegen \u00fcbermitteln statt nur auf dem Weg \u00fcber schl\u00fcssel1\/wert1\/schl\u00fcssel2\/wert2. Besonders im Web und f\u00fcr Formularparameter ist die Kodierung \u00fcber schl\u00fcssel1=wert1&amp;schl\u00fcssel2=wert2 \u00fcblich. Auch das kann in JAX-RS und mit der Jersey-Client-API abgebildet werden:<\/p>\n<ul>\n<li>Anstatt Parameter mit @PathParam zu annotieren, kommt bei Multiwerten @QueryParam zum Einsatz.<\/li>\n<li>Anstatt mit path(String) zu arbeiten, wird bei dem Jersey-Client mit queryParam(String key, Object\u2026 value)<\/li>\n<\/ul>\n<p>Hinweis<\/p>\n<p>Es gibt eine Reihe von Dingen, die in Methoden per Annotation \u00fcbermittelt werden k\u00f6nnen, und nicht nur @PathParam und @QueryParam. Dazu kommen noch Dinge wie @HeaderParam f\u00fcr den HTTP-Request-Header, @CookieParam f\u00fcr Cookies, @Context f\u00fcr Informationsobjekte und weitere Objekte.<\/p>\n<h3>PUT-Anforderungen und das Senden von Daten<\/h3>\n<p>Zum Senden von Daten an einen REST-Service ist die HTTP-PUT-Methode gedacht. Die Implementierung einer Java-Methode kann so aussehen:<\/p>\n<pre><strong>@PUT<\/strong>\n\n@Path( \"message\/{user}\" )\n\n<strong>@Consumes( MediaType.TEXT_PLAIN )<\/strong>\n\npublic Response postMessage( @PathParam( \"user\" ) String user, String message ) {\n\n\u00a0 System.out.printf( \"%s sendet '%s'%n\", user, message );\n\n\u00a0 return Response.noContent().build();\n\n}<\/pre>\n<p>Zun\u00e4chst gilt, dass statt @GET ein @PUT die Methode annotiert. @Consumes h\u00e4lt den MIME-Typ dieser gesendeten Daten fest. Ein zus\u00e4tzlicher @PathParam f\u00e4ngt die Benutzerkennung ein, die dann mit der gesendeten PUT-Nachricht auf der Konsole ausgegeben wird.<\/p>\n<p>Diese beiden Annotationen @PUT und @Consumes sind also n\u00f6tig. Eine R\u00fcckgabe in dem Sinne hat die Methode nicht, und es ist umstritten, ob ein REST-PUT \u00fcberhaupt neben dem Statuscode etwas zur\u00fcckgeben soll. Daher ist die R\u00fcckgabe ein spezielles JAX-RS-Objekt vom Typ Response, das hier f\u00fcr 204, \u201eNo Content\u201c, steht. Ein 204 kommt auch immer dann automatisch zur\u00fcck, wenn eine Methode void als R\u00fcckgabe deklariert.<\/p>\n<h3>PUT\/POST\/DELETE-Sendungen mit der Jersey-Client-API absetzen<\/h3>\n<p>Ein Invocation.Builder bietet neben get(\u2026) auch die anderen Java-Methoden f\u00fcr HTTP-Methoden, also delete(\u2026), post(\u2026), options(\u2026), \u2026 und eben auch put(\u2026) zum Schreiben.<\/p>\n<pre>Client client = ClientBuilder.newClient();\n\nWebTarget target = client.target( \"http:\/\/localhost:8080\/api\" );\n\nResponse response = target.path( \"dating\" ).path( \"message\" )\n\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.path( \"Ha\" ).request().<strong>put( Entity.text(\"Hey Ha!\") )<\/strong>;\n\nSystem.out.println( response );<\/pre>\n<p>Die put(\u2026)-Methode erwartet als Argument den Typ Entity, und zum Objektaufbau gibt es diverse statische Methoden in Entity. Es ist Entity.text(&#8222;Hey Chris&#8220;) eine Abk\u00fcrzung f\u00fcr Entity.entity(&#8222;Hey Chris&#8220;, MediaType.TEXT_PLAIN).<\/p>\n<h3>Versionierung einer REST-API<\/h3>\n<p>Die REST-Schnittstelle ist ein externer Endpunkt einer Software und unterliegt den gleichen Modifikationen wie \u00fcbliche Software: Schnittstellen \u00e4ndern sich, die ausgetauschten Objekte bekommen zus\u00e4tzliche Felder, usw. \u00c4ndert sich die Schnittstelle, erzeugt das eine neue Version. Alte APIs sollte ein Server eine Zeit lang beibehalten, damit nicht von einem Tag auf dem anderen eine vielleicht gro\u00dfe Anzahl von Klienten mit der \u00e4lteren Schnittstelle kommunikationslos sind.<\/p>\n<p>Prinzipiell gibt es drei Ans\u00e4tze bei der Versionierung von REST-APIs; die ersten beiden Varianten nutzen die M\u00f6glichkeiten der URL bzw. des HTTP:<\/p>\n<ol>\n<li>\u00c4nderung der URL, dass etwa eine Versionskennung mit eingebaut wird. Zu finden ist das bei vielen gro\u00dfen Unternehmen, und zu erkennen etwa an der Versionsnummer bei https:\/\/api.twitter.com\/<strong>1<\/strong>\/ https:\/\/www.googleapis.com\/youtube\/<strong>v3<\/strong>\/, https:\/\/api.pinterest.com\/<strong>v1<\/strong>\/, https:\/\/api.instagram.com\/<strong>v1<\/strong>\/.<\/li>\n<li>In einem HTTP-Header, wie Accepts. Dort l\u00e4sst sich die Version in den Dateityp hineinkodieren. Normalerweise ist der Header mit einem MIME-Typ wie text\/plain, text\/html belegt, doch der RFC4288 sieht in Sektion 3.2 einen \u201eVendor Tree\u201c mit dem Pr\u00e4fix vor, sodass sich damit ein eigener Media-Typ inklusive Version formulieren l\u00e4sst. Das kann so aussehen: application\/vnd.tutego.app.api+json;version=2.1 oder application\/vnd.tutego.app.api-v2.1+json.<\/li>\n<li>In den Rumpf einer Nachricht l\u00e4sst sich ebenfalls eine Version einkodieren, zum Beispiel wenn JSON \u00fcbermittelt wird: {&#8222;version&#8220;: &#8222;2.1&#8220;,\u2026}. Nachteilig ist, dass URLs ohne Parameter und ohne K\u00f6rper keinen Platz f\u00fcr eine Versionsnummer lassen.<\/li>\n<\/ol>\n<p>Alle L\u00f6sungen lassen sich prinzipiell mit JAX-RS umsetzen, wobei die L\u00f6sung 1 am Einfachsten ist.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Content-Handler, Marshaller und verschiedene MIME-Typen JAX-RS erlaubt grunds\u00e4tzlich alle MIME-Typen, und die Daten selbst k\u00f6nnen auf verschiedene Java-Datentypen \u00fcbertragen werden. So ist es egal, ob bei Textdokumenten zum Beispiel der R\u00fcckgabetyp String oder OutputStream ist; selbst ein File-Objekt l\u00e4sst sich zur\u00fcckgeben. F\u00fcr einen Parametertyp \u2013 Parameter werden gleich vorgestellt \u2013 gilt das Gleiche: JAX-RS ist [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1,11],"tags":[],"class_list":["post-3933","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3933","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=3933"}],"version-history":[{"count":12,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3933\/revisions"}],"predecessor-version":[{"id":3945,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3933\/revisions\/3945"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=3933"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=3933"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=3933"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}