Apache CXF támogatás a RESTful Web Services számára

1. Áttekintés

Ez az oktatóanyag bemutatja az Apache CXF-et, mint a JAX-RS szabványnak megfelelő keretrendszert, amely meghatározza a Java ökoszisztéma támogatását a REpresentational State Transfer (REST) ​​építészeti mintához.

Pontosabban leírja lépésről lépésre, hogyan lehet RESTful webszolgáltatást létrehozni és közzétenni, valamint hogyan kell egységteszteket írni a szolgáltatás ellenőrzéséhez.

Ez az Apache CXF sorozat harmadik része; az első a CXF használatára összpontosít, mint JAX-WS teljesen kompatibilis megvalósítás. A második cikk útmutatást nyújt a CXF Spring használatához.

2. Maven-függőségek

Az első szükséges függőség az org.apache.cxf: cxf- rt -frontend-jaxrs. Ez a műtárgy biztosítja a JAX-RS API-kat, valamint a CXF megvalósítást:

 org.apache.cxf cxf-rt-frontend-jaxrs 3.1.7 

Ebben az oktatóanyagban a CXF-et használjuk a szerver végpont egy webszolgáltatás közzétételéhez kiszolgálótároló használata helyett. Ezért a következő függőséget kell felvenni a Maven POM fájlba:

 org.apache.cxf cxf-rt-transports-http-móló 3.1.7 

Végül tegyük hozzá a HttpClient könyvtárat az egységtesztek megkönnyítése érdekében:

 org.apache.httpcomponents httpclient 4.5.2 

Itt található a cxf-rt-frontend-jaxrs függőség. Erre a linkre is érdemes hivatkozni a org.apache.cxf: cxf-rt-transport-http-móló leletek. Végül a httpclient itt található.

3. Erőforrásosztályok és leképezési kérelem

Kezdjük egy egyszerű példa megvalósításával; két erőforrással fel fogjuk állítani a REST API-t Tanfolyam és Diák.

Kezdjük egyszerűen, és haladunk egy összetettebb példa felé.

3.1. Az erőforrások

Itt van a Diák erőforrásosztály:

@XmlRootElement (name = "Student") public class Student {private int id; privát karakterlánc neve; // standard getterek és beállítók // standard egyenlő és hashCode megvalósítások}

Vegye észre, hogy a @XmlRootElement kommentár a JAXB számára, hogy ennek az osztálynak az példányait át kell vezetni az XML-be.

Ezután következik a Tanfolyam erőforrásosztály:

@XmlRootElement (name = "Course") public class Course {private int id; privát karakterlánc neve; magánlista hallgatók = new ArrayList (); privát Student findById (int id) {for (Student student: students) {if (student.getId () == id) {visszatérő hallgató; }} return null; }
 // standard getters és setters // standard egyenlő és hasCode implementációk}

Végül hajtsuk végre a CourseRepository - amely a gyökérerőforrás és a webszolgáltatás erőforrásainak belépési pontjaként szolgál:

@Path ("tanfolyam") @Produces ("text / xml") nyilvános osztály CourseRepository {private Map tanfolyamok = new HashMap (); // kéréskezelési módszerek privát Course findById (int id) {for (Map.Entry course: courses.entrySet ()) {if (course.getKey () == id) {return course.getValue (); }} return null; }}

Figyelje meg a @Pálya annotáció. A CourseRepository itt a gyökérerőforrás, ezért leképezve van a kezdő összes URL kezelésére tanfolyam.

Az értéke @Termékek az annotációt arra használják, hogy a kiszolgáló megmondja, hogy az osztályon belüli módszerekből visszaküldött objektumokat konvertálja XML dokumentumokká, mielőtt azokat elküldené az ügyfeleknek. Itt a JAXB-t használjuk alapértelmezettként, mivel más kötési mechanizmus nincs megadva.

3.2. Egyszerű adatbeállítás

Mivel ez egy egyszerű megvalósítási példa, a memóriában lévő adatokat használjuk a teljes értékű, tartós megoldás helyett.

Ezt szem előtt tartva valósítsunk meg néhány egyszerű beállítási logikát az adatok feltöltésére a rendszerbe:

{Hallgató diák1 = új Tanuló (); Student student2 = new Student (); hallgató1.setId (1); student1.setName ("A tanuló"); hallgató2.setId (2); student2.setName ("B hallgató"); List1 tanuló = új ArrayList (); tanfolyam1Diákok.add (hallgató1); tanfolyam1Diákok.add (hallgató2); Tanfolyam1 = új tanfolyam (); Tanfolyam tanfolyam2 = new Course (); tanfolyam1.setId (1); course1.setName ("REST with Spring"); course1.setStudents (course1Students); tanfolyam2.setId (2); course2.setName ("Ismerje meg a tavaszi biztonságot"); tanfolyamok.put (1, tanfolyam1); tanfolyamok.put (2, tanfolyam2); }

Az osztály ezen osztályán belüli, a HTTP-kéréseket kezelő módszereket a következő alfejezet tárgyalja.

3.3. Az API - lekérdezési metódusok

Most térjünk rá a tényleges REST API megvalósítására.

Elkezdjük hozzáadni az API műveleteket - a @Pálya annotáció - közvetlenül a POJO forrásban.

Fontos megérteni, hogy ez jelentős különbség a tipikus tavaszi projekt megközelítésétől - ahol az API-műveleteket egy vezérlőben kell meghatározni, nem magában a POJO-ban.

Kezdjük a Tanfolyam osztály:

@GET @Path ("{studentId}") nyilvános Student getStudent (@PathParam ("studentId") int studentId) {return findById (studentId); }

Egyszerűen fogalmazva: a metódust a kezelés során hívják meg KAP kérések, amelyeket a @KAP annotáció.

Észrevette a Diákigazolvány elérési út paraméter a HTTP kérésből.

Ezután egyszerűen használjuk a findById segítő módszer a megfelelő visszaadására Diák példa.

A következő módszer kezeli POST kérelmek, amelyeket a @POST kommentár, a kapott adatok hozzáadásával Diák kifogásolja a diákok lista:

@POST @Path ("") public Response createStudent (Student student) {for (Student element: students) {if (element.getId () == student.getId () {return Response.status (Response.Status.CONFLICT) .build ();}} students.add (hallgató); return Response.ok (hallgató) .build ();}

Ez eredményezi a 200 OK válasz, ha a létrehozási művelet sikeres volt, vagy 409 Konfliktus ha egy tárgy a beküldött id már létezik.

Vegye figyelembe azt is, hogy kihagyhatjuk a @Pálya kommentár, mivel értéke üres karakterlánc.

Az utolsó módszer gondoskodik róla TÖRÖL kéréseket. Eltávolít egy elemet a diákok felsorolja, kinek id a kapott útvonal paraméter, és a választ adja vissza rendben (200) állapot. Abban az esetben, ha a megadott elemhez nincsenek társítva elemek id, ami azt jelenti, hogy nincs mit eltávolítani, ez a módszer egy választ ad vissza Nem található (404) állapot:

@DELETE @Path ("{studentId}") nyilvános válasz deleteStudent (@PathParam ("studentId") int studentId) {Student student = findById (studentId); if (hallgató == null) {return Válasz.állapot (Válasz.állapot.NOT_FOUND) .build (); } diákok.eltávolítás (tanuló); return Response.ok (). build (); }

Térjünk át a CourseRepository osztály.

A következő getCourse metódus a Tanfolyam objektum, amely egy bejegyzés értéke a tanfolyamok térkép, amelynek kulcsa a fogadott courseId elérési útjának paramétere KAP kérés. Belsőleg a módszer elküldi az elérési út paramétereit a findById segítő módszer munkájának elvégzéséhez.

@GET @Path ("tanfolyamok / {courseId}") nyilvános tanfolyam getCourse (@PathParam ("courseId") int courseId) {return findById (courseId); }

A következő módszer frissíti a tanfolyamok térkép, ahol a test a fogadott PUT a kérés a bejegyzés értéke és a courseId paraméter a társított kulcs:

@PUT @Path ("courses / {courseId}") public Response updateCourse (@PathParam ("courseId") int courseId, Course course) {Course existingCourse = findById (courseId); if (existingCourse == null) {return Response.status (Response.Status.NOT_FOUND) .build (); } if (existingCourse.equals (tanfolyam)) {return Response.notModified (). build (); } tanfolyamok.put (courseId, tanfolyam); return Válasz.ok (). build (); }

Ez updateCourse metódus választ ad vissza rendben (200) állapot, ha a frissítés sikeres, semmit sem változtat, és visszaadja a Nincs módosítva (304) válasz, ha a meglévő és a feltöltött objektumok ugyanazokkal a mezőértékekkel rendelkeznek. Ha a Tanfolyam példány az adott id nem található a tanfolyamok map, a metódus választ ad vissza Nem található (404) állapot.

Ennek a gyökér erőforrás osztálynak a harmadik módszere nem kezel közvetlenül HTTP kéréseket. Ehelyett kéréseket delegál a Tanfolyam osztály, ahol a kéréseket egyeztetési módszerekkel kezelik:

@Path ("tanfolyamok / {courseId} / hallgatók") nyilvános Course pathToStudent (@PathParam ("courseId") int courseId) {return findById (courseId); }

Megmutattuk a módszereket a Tanfolyam osztály, amely közvetlenül azelőtt feldolgozta a kérelmeket.

4. szerver Végpont

Ez a szakasz egy CXF szerver felépítésére összpontosít, amelyet a RESTful webszolgáltatás közzétételére használnak, amelynek erőforrásait az előző szakasz ismerteti. Az első lépés az a JAXRSServerFactoryBean objektumot és állítsa be a gyökér erőforrás osztályt:

JAXRSServerFactoryBean factoryBean = új JAXRSServerFactoryBean (); factoryBean.setResourceClasses (CourseRepository.class);

Ezután erőforrás-szolgáltatót kell beállítani a gyárban, hogy kezelje a gyökér erőforrásosztály életciklusát. Az alapértelmezett szingulett erőforrás-szolgáltatót használjuk, amely ugyanazt az erőforrás-példányt adja vissza minden kéréshez:

factoryBean.setResourceProvider (új SingletonResourceProvider (új CourseRepository ()));

Címet is beállítottunk annak az URL-nek a megjelölésére, ahol a webszolgáltatás megjelent:

factoryBean.setAddress ("// localhost: 8080 /");

Most a gyárBab felhasználható egy új létrehozására szerver amely elkezdi hallgatni a bejövő kapcsolatokat:

Kiszolgálószerver = factoryBean.create ();

Az ebben a szakaszban található összes fenti kódot be kell csomagolni a fő- módszer:

public class RestfulServer {public static void main (String args []) dobja a Kivételt {// fent látható kódrészletek}}

Ennek megidézése fő- módszert a 6. szakasz mutatja be.

5. Tesztesetek

Ez a szakasz a korábban létrehozott webszolgáltatás érvényesítéséhez használt teszteseteket ismerteti. Ezek a tesztek validálják a szolgáltatás erőforrásállapotát, miután megválaszolták a négy leggyakrabban használt módszer, nevezetesen HTTP kéréseit KAP, POST, PUT, és TÖRÖL.

5.1. Készítmény

Először két statikus mezőt deklarálunk a tesztosztályon belül, megnevezve RestfulTest:

privát statikus karakterlánc BASE_URL = "// localhost: 8080 / baeldung / courses /"; privát statikus CloseableHttpClient kliens;

A tesztek lefuttatása előtt létrehozzuk a ügyfél objektum, amelyet a szerverrel való kommunikációhoz és utólagos megsemmisítéshez használnak:

@BeforeClass public static void createClient () {client = HttpClients.createDefault (); } @AfterClass public static void closeClient () dobja az IOException {client.close (); }

A ügyfél a példány készen áll a tesztesetek használatára.

5.2. KAP Kérések

A teszt osztályban két módszert határozunk meg az elküldésre KAP kéréseket a webszolgáltatást futtató kiszolgálóhoz.

Az első módszer az, hogy a Tanfolyam adott esetben id az erőforrásban:

privát tanfolyam getCourse (int courseOrder) dobja az IOException {URL url = új URL (BASE_URL + courseOrder); InputStream input = url.openStream (); Tanfolyam = JAXB.unmarshal (új InputStreamReader (input), Course.class); visszatérő tanfolyam; }

A második az, hogy a Diák példában adott idA kurzus és a hallgató az erőforrásban:

privát Student getStudent (int courseOrder, int studentOrder) dobja az IOException {URL url = új URL-t (BASE_URL + courseOrder + "/ students /" + studentOrder); InputStream input = url.openStream (); Diák diák = JAXB.unmarshal (új InputStreamReader (input), Student.class); visszatérő hallgató; }

Ezek a módszerek HTTP-t küldenek KAP kéréseket a szolgáltatási erőforráshoz, majd unmarhalálos XML válaszokat a megfelelő osztályok példányaira. Mindkettőt a szolgáltatás erőforrásainak állapotának ellenőrzésére használják végrehajtás után POST, PUT, és TÖRÖL kéréseket.

5.3. POST Kérések

Ez az alfejezet két tesztesetet tartalmaz POST kéréseket, bemutatva a webszolgáltatás működését a feltöltéskor Diák példány konfliktushoz vezet, és ha sikeresen létrejön.

Az első tesztben a Diák tárgy szétmarcangolva a konfliktus_student.xml fájl, amely az osztályúton található, a következő tartalommal:

 2 B tanuló 

Így alakul át a tartalom a-vá POST kérelmező testület:

HttpPost httpPost = new HttpPost (BASE_URL + "1 / diák"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("context_student.xml"); httpPost.setEntity (új InputStreamEntity (resourceStream));

A Tartalom típus fejléc úgy van beállítva, hogy elmondja a szervernek, hogy a kérés tartalomtípusa XML:

httpPost.setHeader ("Tartalom-típus", "text / xml");

Mivel a feltöltött Diák objektum már létezik az elsőben Tanfolyam például arra számítunk, hogy a létrehozás meghiúsul, és a Konfliktus (409) állapotot adunk vissza. A következő kódrészlet igazolja az elvárást:

HttpResponse response = client.execute (httpPost); assertEquals (409, response.getStatusLine (). getStatusCode ());

A következő tesztben kivonjuk a HTTP kérés törzsét egy nevű fájlból created_student.xml, szintén az osztályúton. Itt található a fájl tartalma:

 3 C tanuló 

Az előző tesztesethez hasonlóan létrehozunk és végrehajtunk egy kérést, majd ellenőrizzük, hogy egy új példány sikeresen létrejött-e:

HttpPost httpPost = new HttpPost (BASE_URL + "2 / diák"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("created_student.xml"); httpPost.setEntity (új InputStreamEntity (resourceStream)); httpPost.setHeader ("Content-Type", "text / xml"); HttpResponse response = client.execute (httpPost); assertEquals (200, response.getStatusLine (). getStatusCode ());

Megerősíthetjük a webszolgáltatás erőforrásának új állapotait:

Diák diák = getStudent (2, 3); assertEquals (3, hallgató.getId ()); assertEquals ("C tanuló", student.getName ());

Erre válaszol az XML az új kérésére Diák Az objektum így néz ki:

  3 C tanuló 

5.4. PUT Kérések

Kezdjük egy érvénytelen frissítési kérelemmel, ahol a Tanfolyam a frissítendő objektum nem létezik. Itt található a nem létező helyettesítésére használt példány tartalma Tanfolyam objektum a webszolgáltatás erőforrásában:

 3 Apache CXF támogatás a RESTful számára 

Ezt a tartalmat az úgynevezett fájl tárolja non_existent_course.xml az osztályúton. Kivonják, majd felhasználják a PUT kérés az alábbi kóddal:

HttpPut httpPut = new HttpPut (BASE_URL + "3"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("non_existent_course.xml"); httpPut.setEntity (új InputStreamEntity (resourceStream));

A Tartalom típus fejléc úgy van beállítva, hogy elmondja a szervernek, hogy a kérés tartalomtípusa XML:

httpPut.setHeader ("Tartalom-típus", "text / xml");

Mivel szándékosan érvénytelen kérést küldtünk egy nem létező objektum frissítésére, a Nem található (404) válasz várhatóan érkezik. A válasz érvényesül:

HttpResponse response = client.execute (httpPut); assertEquals (404, response.getStatusLine (). getStatusCode ());

A második tesztesetben a PUT kéréseket, benyújtjuk a Tanfolyam objektum ugyanazokkal a mezőértékekkel. Mivel ebben az esetben semmi sem változik, arra számítunk, hogy erre választ adunk Nincs módosítva (304) állapotot adunk vissza. Az egész folyamatot szemlélteti:

HttpPut httpPut = új HttpPut (BASE_URL + "1"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("változatlan_pálya.xml"); httpPut.setEntity (új InputStreamEntity (resourceStream)); httpPut.setHeader ("Tartalom-típus", "text / xml"); HttpResponse response = client.execute (httpPut); assertEquals (304, response.getStatusLine (). getStatusCode ());

Hol unchanged_course.xml az osztálypályán található fájl, amely a frissítéshez használt információkat tárolja. Itt van a tartalma:

 1 Pihenés tavasszal 

A legutóbbi tüntetésen PUT kéréseket, érvényes frissítést hajtunk végre. Az alábbiakban a megváltozott_tanfolyam.xml fájl, amelynek tartalmát frissítik a Tanfolyam példány a webszolgáltatás erőforrásában:

 2 Apache CXF támogatás a RESTful számára 

A kérés így épül fel és hajtódik végre:

HttpPut httpPut = new HttpPut (BASE_URL + "2"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("megváltozott_tanfolyam.xml"); httpPut.setEntity (új InputStreamEntity (resourceStream)); httpPut.setHeader ("Tartalom-típus", "text / xml");

Érvényesítsük a PUT kérjen a szerverről, és érvényesítse a sikeres feltöltést:

HttpResponse response = client.execute (httpPut); assertEquals (200, response.getStatusLine (). getStatusCode ());

Ellenőrizzük a webszolgáltatás erőforrásának új állapotait:

Tanfolyam = getCourse (2); assertEquals (2, course.getId ()); assertEquals ("Apache CXF támogatás a RESTful számára", course.getName ());

A következő kódrészlet az XML-válasz tartalmát mutatja, amikor GET-kérés érkezik a korábban feltöltöttre Tanfolyam objektum elküldve:

  2 Apache CXF támogatás a RESTful számára 

5.5. TÖRÖL Kérések

Először próbáljunk meg törölni egy nem létezőt Diák példa. A műveletnek sikertelennek kell lennie, és ennek megfelelő választ kell adnia Nem található (404) állapot várható:

HttpDelete httpDelete = new HttpDelete (BASE_URL + "1 / students / 3"); HttpResponse response = client.execute (httpDelete); assertEquals (404, response.getStatusLine (). getStatusCode ());

A második tesztesetben a TÖRÖL kéréseket létrehozunk, végrehajtunk és ellenőrizünk egy kérést:

HttpDelete httpDelete = new HttpDelete (BASE_URL + "1 / students / 1"); HttpResponse response = client.execute (httpDelete); assertEquals (200, response.getStatusLine (). getStatusCode ());

A webszolgáltatás erőforrásának új állapotait a következő kódrészlettel ellenőrizzük:

Tanfolyam = getCourse (1); assertEquals (1, természetesen.getStudents (). méret ()); assertEquals (2, course.getStudents (). get (0) .getId ()); assertEquals ("B tanuló", course.getStudents (). get (0) .getName ());

Ezután felsoroljuk az első kérése után kapott XML választ Tanfolyam objektum a webszolgáltatás erőforrásában:

  1 Pihenés a 2. tavaszi B tanulóval 

Világos, hogy az első Diák sikeresen eltávolítva.

6. Teszt végrehajtása

A 4. szakasz leírta, hogyan lehet létrehozni és megsemmisíteni a szerver például a fő- módszere RestfulServer osztály.

A szerver üzembe helyezésének utolsó lépése ennek meghívása fő- módszer. Ennek elérése érdekében az Exec Maven plugint tartalmazza és konfigurálja a Maven POM fájl:

 org.codehaus.mojo exec-maven-plugin 1.5.0 com.baeldung.cxf.jaxrs.implementation.RestfulServer 

A plugin legújabb verziója ezen a linken található.

Az ebben az oktatóanyagban bemutatott műtárgy összeállításának és csomagolásának folyamatában a Maven Surefire plugin automatikusan végrehajtja az osztályokba zárt összes tesztet, amelynek neve kezdődik Teszt. Ebben az esetben a bővítményt úgy kell konfigurálni, hogy kizárja ezeket a teszteket:

 maven-surefire-plugin 2.19.1 ** / ServiceTest 

A fenti konfigurációval ServiceTest kizárt, mivel ez a tesztosztály neve. Bármely nevet választhat az osztályhoz, feltéve, hogy az abban szereplő teszteket nem a Maven Surefire plugin futtatja, mielőtt a szerver készen áll a kapcsolatokra.

A Maven Surefire plugin legújabb verzióját itt ellenőrizheti.

Most végre tudja hajtani a exec: java cél a RESTful webszolgáltatás kiszolgálójának elindítása, majd az IDE használatával futtassa a fenti teszteket. Ezzel egyenértékűen a parancs végrehajtásával is elindíthatja a tesztet mvn -Dtest = ServiceTest teszt terminálban.

7. Következtetés

Ez az oktatóanyag bemutatta az Apache CXF használatát JAX-RS megvalósításként. Bemutatta, hogy a keretrendszer hogyan használható fel a RESTful webszolgáltatás erőforrásainak meghatározására és egy szerver létrehozására a szolgáltatás közzétételéhez.

Mindezen példák és kódrészletek megvalósítása megtalálható a GitHub projektben.