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.