Podrška Apache CXF za RESTful web usluge

1. Pregled

Ovaj vodič predstavlja Apache CXF kao okvir sukladan standardu JAX-RS, koji definira podršku Java ekosustava za arhitektonski obrazac REpresentational State Transfer (REST).

Točnije, opisuje korak po korak kako konstruirati i objaviti RESTful web uslugu i kako napisati jedinične testove za provjeru usluge.

Ovo je treća u nizu na Apache CXF; prvi se fokusira na uporabu CXF-a kao JAX-WS-ove potpuno sukladne implementacije. Drugi članak pruža vodič o tome kako koristiti CXF s Springom.

2. Ovisnosti Mavena

Prva potrebna ovisnost je org.apache.cxf: cxf- rt -frontend-jaxrs. Ovaj artefakt nudi JAX-RS API-je, kao i CXF implementaciju:

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

U ovom uputstvu koristimo CXF za stvaranje a Poslužitelj krajnja točka za objavljivanje web usluge umjesto da koristi spremnik servleta. Stoga je u datoteku Maven POM potrebno uključiti sljedeću ovisnost:

 org.apache.cxf cxf-rt-transports-http-jetty 3.1.7 

Na kraju, dodajmo knjižnicu HttpClient kako bismo olakšali jedinične testove:

 org.apache.httpkomponente httpclient 4.5.2 

Ovdje možete pronaći najnoviju verziju cxf-rt-frontend-jaxrs ovisnost. Možda ćete htjeti pogledati ovu vezu za najnovije verzije org.apache.cxf: cxf-rt-transports-http-jetty artefakti. Napokon, najnovija verzija httpclient možete pronaći ovdje.

3. Razredi resursa i mapiranje zahtjeva

Počnimo s primjenom jednostavnog primjera; postavit ćemo naš REST API s dva resursa Tečaj i Student.

Krenut ćemo od jednostavnog i kretati se prema složenijim primjerima.

3.1. Resursi

Evo definicije Student klasa resursa:

@XmlRootElement (name = "Student") javni razred Student {private int id; privatni naziv niza; // standardni getteri i postavljači // standard equals i implementacije hashCode}

Primijetimo da koristimo @XmlRootElement napomena da se JAXB-u kaže da instance ove klase trebaju biti marširane u XML.

Dalje, dolazi definicija Tečaj klasa resursa:

@XmlRootElement (name = "Course") tečaj javne klase {private int id; privatni naziv niza; privatni popis učenika = novi ArrayList (); private Student findById (int id) {for (Student student: students) {if (student.getId () == id) {return student; }} return null; }
 // standardni getteri i postavljači // standardne jednake i implementacije hasCode}

Napokon, provedimo Repozitorij tečaja - koji je osnovni resurs i služi kao ulazna točka u resurse web usluga:

@Path ("course") @Produces ("text / xml") javna klasa CourseRepository {tečajevi privatne mape = novi HashMap (); // metode obrade zahtjeva private Course findById (int id) {for (Map.Entry course: courses.entrySet ()) {if (course.getKey () == id) {return course.getValue (); }} return null; }}

Primijetite mapiranje s @Staza bilješka. The Repozitorij tečaja je korijenski resurs ovdje, pa je preslikan da obrađuje sve URL-ove počevši od tečaj.

Vrijednost @Proizvodi anotacija se koristi kako bi poslužitelju rekla da pretvori objekte vraćene iz metoda iz ove klase u XML dokumente prije slanja klijentima. Ovdje koristimo JAXB kao zadani jer nisu navedeni drugi mehanizmi vezanja.

3.2. Jednostavno postavljanje podataka

Budući da je ovo jednostavan primjer implementacije, koristimo podatke u memoriji umjesto punopravnog trajnog rješenja.

Imajući to na umu, implementirajmo jednostavnu logiku postavljanja kako bismo neke podatke unijeli u sustav:

{Student student1 = novi Student (); Student student2 = novi Student (); student1.setId (1); student1.setName ("Student A"); student2.setId (2); student2.setName ("Student B"); Popis course1Students = new ArrayList (); course1Students.add (student1); course1Students.add (student2); Tečaj tečaja1 = novi tečaj (); Tečaj tečaja2 = novi tečaj (); course1.setId (1); course1.setName ("ODMOR uz proljeće"); course1.setStudents (course1Students); course2.setId (2); course2.setName ("Nauči proljetnu sigurnost"); courses.put (1, course1); courses.put (2, course2); }

Metode u ovoj klasi koje se brinu za HTTP zahtjeve obrađene su u sljedećem pododjeljku.

3.3. API - Metode mapiranja zahtjeva

Idemo sada na implementaciju stvarnog API-ja REST.

Počet ćemo dodavati API operacije - pomoću @Staza napomena - točno u POJO-ima resursa.

Važno je shvatiti da je to značajna razlika od pristupa u tipičnom proljetnom projektu - gdje bi API operacije bile definirane u kontroleru, a ne na samom POJO-u.

Počnimo s metodama mapiranja definiranim unutar Tečaj razred:

@GET @Path ("{studentId}") javni student getStudent (@PathParam ("studentId") int studentId) {return findById (studentId); }

Jednostavno rečeno, metoda se poziva prilikom rukovanja DOBITI zahtjevi, označeni s @DOBITI bilješka.

Primijetio sam jednostavnu sintaksu mapiranja studentska iskaznica parametar puta iz HTTP zahtjeva.

Tada jednostavno koristimo findById pomoćna metoda za vraćanje odgovarajuće Student primjer.

Sljedeća metoda obrađuje OBJAVI zahtjevi, označeni s @POST napomena, dodavanjem primljenog Student prigovoriti na studenti popis:

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

Ovo vraća a 200 OK odgovor ako je operacija izrade bila uspješna, ili 409 Sukob ako objekt s podnesenim iskaznica već postoji.

Također imajte na umu da možemo preskočiti @Staza napomena jer je njezina vrijednost prazan niz.

Zadnja metoda se brine IZBRISATI zahtjevi. Uklanja element iz studenti popis čija iskaznica je primljeni parametar puta i vraća odgovor s u redu (200) status. U slučaju da s navedenim nema pridruženih elemenata iskaznica, što podrazumijeva da se ne smije ništa ukloniti, ova metoda vraća odgovor s Nije pronađeno (404) status:

@DELETE @Path ("{studentId}") javni odgovor deleteStudent (@PathParam ("studentId") int studentId) {Student student = findById (studentId); if (student == null) {return Response.status (Response.Status.NOT_FOUND) .build (); } students.remove (student); povratak Response.ok (). build (); }

Prijeđimo na zahtjev za metodama mapiranja Repozitorij tečaja razred.

Sljedeće getCourse metoda vraća a Tečaj objekt koji je vrijednost unosa u tečajevi karta čiji je ključ primljeni naravnoId parametar puta a DOBITI zahtjev. Metoda interno šalje parametre puta na findById pomoćna metoda da radi svoj posao.

@GET @Path ("courses / {courseId}") javni tečaj getCourse (@PathParam ("courseId") int courseId) {return findById (courseId); }

Sljedeća metoda ažurira postojeći unos datoteke tečajevi karta, gdje je tijelo primljenog STAVITI zahtjev je vrijednost unosa i naravnoId parametar je pridruženi ključ:

@PUT @Path ("courses / {courseId}") public Response updateCourse (@PathParam ("courseId") int courseId, tečaj Tečaja) {Tečaj postojećiKurs = findById (courseId); if (existingCourse == null) {return Response.status (Response.Status.NOT_FOUND) .build (); } if (existingCourse.equals (course)) {return Response.notModified (). build (); } courses.put (courseId, course); povratak Response.ok (). build (); }

Ovaj updateCourse metoda vraća odgovor s u redu Status (200) ako je ažuriranje uspješno, ne mijenja ništa i vraća a Nije izmijenjeno (304) odgovor ako postojeći i preneseni objekti imaju iste vrijednosti polja. U slučaju da a Tečaj instanca s danim iskaznica nije pronađen u tečajevi map, metoda vraća odgovor s Nije pronađeno (404) status.

Treća metoda ove klase korijenskih resursa ne obrađuje izravno nijedan HTTP zahtjev. Umjesto toga, on delegira zahtjeve na Tečaj klasa u kojoj se zahtjevi obrađuju metodama podudaranja:

@Path ("courses / {courseId} / students") javni tečaj pathToStudent (@PathParam ("courseId") int courseId) {return findById (courseId); }

Pokazali smo metode unutar Tečaj klase koja neposredno prije obrađuje delegirane zahtjeve.

4. Poslužitelj Krajnja točka

Ovaj se dio usredotočuje na izgradnju CXF poslužitelja koji se koristi za objavljivanje RESTful web usluge čiji su resursi prikazani u prethodnom odjeljku. Prvi korak je instanciranje a JAXRSServerFactoryBean objekt i postaviti osnovnu klasu resursa:

JAXRSServerFactoryBean factoryBean = novo JAXRSServerFactoryBean (); factoryBean.setResourceClasses (CourseRepository.class);

Tada treba na tvorničkom grahu postaviti davatelja resursa za upravljanje životnim ciklusom korijenske klase resursa. Koristimo zadani singleton davatelj resursa koji vraća istu instancu resursa svakom zahtjevu:

factoryBean.setResourceProvider (novi SingletonResourceProvider (novi CourseRepository ()));

Također smo postavili adresu koja označava URL na kojem je web usluga objavljena:

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

Sada factoryBean može se koristiti za stvaranje novog poslužitelju koji će početi slušati dolazne veze:

Poslužitelj poslužitelja = factoryBean.create ();

Sav gornji kod u ovom odjeljku trebao bi biti umotan u glavni metoda:

javna klasa RestfulServer {public static void main (String args []) baca iznimku {// isječci koda prikazani gore}}

Zazivanje ovoga glavni metoda predstavljena je u odjeljku 6.

5. Ispitni slučajevi

Ovaj odjeljak opisuje test slučajeve koji se koriste za provjeru valjanosti web usluge koju smo prije stvorili. Ti testovi potvrđuju stanja resursa usluge nakon što su odgovorili na HTTP zahtjeve četiri najčešće korištene metode, naime DOBITI, OBJAVI, STAVITI, i IZBRISATI.

5.1. Priprema

Prvo se unutar klase ispitivanja imenuju dva statička polja RestfulTest:

privatni statički niz BASE_URL = "// localhost: 8080 / baeldung / courses /"; privatni statički klijent CloseableHttpClient;

Prije pokretanja testova kreiramo klijent objekt koji se koristi za komunikaciju s poslužiteljem i njegovo uništavanje nakon toga:

@BeforeClass javna statička praznina createClient () {client = HttpClients.createDefault (); } @AfterClass javna statička void closeClient () baca IOException {client.close (); }

The klijent instanca je sada spremna za upotrebu u testnim slučajevima.

5.2. DOBITI Zahtjevi

U testnoj klasi definiramo dvije metode za slanje DOBITI zahtjeva prema poslužitelju koji izvodi web uslugu.

Prva metoda je dobivanje a Tečaj primjer s obzirom na svoj iskaznica u resursu:

privatni tečaj getCourse (int courseOrder) baca IOException {URL url = novi URL (BASE_URL + courseOrder); InputStream input = url.openStream (); Tečaj kolegija = JAXB.unmarshal (novi InputStreamReader (ulaz), Course.class); povratni tečaj; }

Drugo je dobiti a Student primjer s obzirom na iskaznicas predmeta i studenta u resursu:

private Student getStudent (int courseOrder, int studentOrder) baca IOException {URL url = novi URL (BASE_URL + courseOrder + "/ students /" + studentOrder); InputStream input = url.openStream (); Student student = JAXB.unmarshal (novi InputStreamReader (ulaz), Student.class); povratak student; }

Ove metode šalju HTTP DOBITI zahtjeva za resursom usluge, a zatim neuobičajeni XML odgovori na instance odgovarajućih klasa. Obje se koriste za provjeru stanja resursa usluge nakon izvršavanja OBJAVI, STAVITI, i IZBRISATI zahtjevi.

5.3. OBJAVI Zahtjevi

Ovaj pododjeljak sadrži dva testna slučaja za OBJAVI zahtjeva koji ilustriraju rad web usluge prilikom prijenosa Student instanca dovodi do sukoba i kada je uspješno stvoren.

U prvom testu koristimo a Student objekt nemarširan iz sukob_student.xml datoteka koja se nalazi na stazi sa sljedećim sadržajem:

 2 Student B 

Tako se taj sadržaj pretvara u OBJAVI tijelo zahtjeva:

HttpPost httpPost = novi HttpPost (BASE_URL + "1 / studenti"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("sukob_studija.xml"); httpPost.setEntity (novi InputStreamEntity (resourceStream));

The Vrsta sadržaja zaglavlje je postavljeno da govori poslužitelju da je vrsta sadržaja zahtjeva XML:

httpPost.setHeader ("Content-Type", "text / xml");

Budući da je postavljeno Student objekt već postoji u prvom Tečaj Primjerice, očekujemo da izrada ne uspije i odgovor sa Sukob Vraćen je status (409). Sljedeći isječak koda potvrđuje očekivanje:

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

U sljedećem testu izvlačimo tijelo HTTP zahtjeva iz datoteke s imenom created_student.xml, također na stazi. Evo sadržaja datoteke:

 3 Student C 

Slično prethodnom testnom slučaju, izrađujemo i izvršavamo zahtjev, a zatim provjeravamo je li uspješno izrađena nova instanca:

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

Možemo potvrditi nova stanja resursa web usluga:

Student student = getStudent (2, 3); assertEquals (3, student.getId ()); assertEquals ("Student C", student.getName ());

To je ono što XML odgovara na zahtjev za novim Student objekt izgleda ovako:

  3 Student C 

5.4. STAVITI Zahtjevi

Počnimo s nevažećim zahtjevom za ažuriranje, gdje je Tečaj objekt koji se ažurira ne postoji. Ovdje je sadržaj instance koja se koristi za zamjenu nepostojeće Tečaj objekt u resursu web usluge:

 3 Apache CXF podrška za RESTful 

Taj se sadržaj sprema u datoteku pod nazivom nepostojeći_kurs.xml na razrednoj stazi. Izvlači se i zatim koristi za naseljavanje tijela a STAVITI zahtjev prema donjem kodu:

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

The Vrsta sadržaja zaglavlje je postavljeno da govori poslužitelju da je vrsta sadržaja zahtjeva XML:

httpPut.setHeader ("Content-Type", "text / xml");

Budući da smo namjerno poslali nevaljani zahtjev za ažuriranje nepostojećeg objekta, a Nije pronađeno (404) očekuje se primanje odgovora. Odgovor je potvrđen:

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

U drugom testnom slučaju za STAVITI zahtjeve, podnosimo a Tečaj objekt s istim vrijednostima polja. Budući da se u ovom slučaju ništa ne mijenja, očekujemo odgovor s Nije izmijenjeno Vraćen je status (304). Cijeli postupak je ilustriran:

HttpPut httpPut = novi HttpPut (BASE_URL + "1"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("unchanged_course.xml"); httpPut.setEntity (novi InputStreamEntity (resourceStream)); httpPut.setHeader ("Content-Type", "text / xml"); HttpResponse odgovor = client.execute (httpPut); assertEquals (304, response.getStatusLine (). getStatusCode ());

Gdje nepromijenjen_kurs.xml je datoteka na putu predavanja koja čuva podatke koji se koriste za ažuriranje. Evo njegovog sadržaja:

 1 ODMOR s proljećem 

U posljednjoj demonstraciji STAVITI zahtjeva izvršavamo valjano ažuriranje. Slijedi sadržaj promijenio_kurs.xml datoteka čiji se sadržaj koristi za ažuriranje a Tečaj primjer u resursu web usluge:

 2 Apache CXF podrška za RESTful 

Zahtjev se gradi i izvršava na sljedeći način:

HttpPut httpPut = novi HttpPut (BASE_URL + "2"); InputStream resourceStream = this.getClass (). GetClassLoader () .getResourceAsStream ("changed_course.xml"); httpPut.setEntity (novi InputStreamEntity (resourceStream)); httpPut.setHeader ("Content-Type", "text / xml");

Provjerimo a STAVITI zahtjev poslužitelju i provjeru uspješnog prijenosa:

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

Provjerimo nova stanja resursa web usluga:

Tečaj tečaja = getCourse (2); assertEquals (2, course.getId ()); assertEquals ("Apache CXF podrška za RESTful", course.getName ());

Sljedeći isječak koda prikazuje sadržaj XML odgovora kada GET zahtjev za prethodno prenesene Tečaj objekt je poslan:

  2 Apache CXF podrška za RESTful 

5.5. IZBRISATI Zahtjevi

Prvo, pokušajmo izbrisati nepostojeće Student primjer. Operacija ne bi trebala uspjeti i odgovarajući odgovor s Nije pronađeno Očekuje se status (404):

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

U drugom testnom slučaju za IZBRISATI zahtjeva, kreiramo, izvršavamo i provjeravamo zahtjev:

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

Sljedećim isječkom koda provjeravamo nova stanja resursa web usluga:

Tečaj tečaja = getCourse (1); assertEquals (1, course.getStudents (). size ()); assertEquals (2, course.getStudents (). get (0) .getId ()); assertEquals ("Student B", course.getStudents (). get (0) .getName ());

Dalje, navodimo XML odgovor koji je primljen nakon zahtjeva za prvi Tečaj objekt u resursu web usluge:

  1 ODMOR sa Spring 2 Student B 

Jasno je da prvi Student je uspješno uklonjen.

6. Izvršenje testa

Odjeljak 4 opisao je kako stvoriti i uništiti a Poslužitelj primjer u glavni metoda RestfulServer razred.

Posljednji korak za pokretanje poslužitelja je njegovo pozivanje glavni metoda. Da bi se to postiglo, dodatak Exec Maven uključen je i konfiguriran u Maven POM datoteci:

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

Najnoviju verziju ovog dodatka možete pronaći putem ove veze.

U procesu sastavljanja i pakiranja artefakta prikazanog u ovom vodiču, dodatak Maven Surefire automatski izvršava sve testove zatvorene u razredima s imenima koja počinju ili završavaju Test. Ako je to slučaj, dodatak bi trebao biti konfiguriran tako da isključuje te testove:

 maven-surefire-plugin 2.19.1 ** / ServiceTest 

Uz gornju konfiguraciju, ServiceTest je isključeno jer je to naziv klase ispitivanja. Možete odabrati bilo koje ime za tu klasu, pod uvjetom da dodaci Maven Surefire ne izvodi testove sadržane u njoj prije nego što poslužitelj bude spreman za veze.

Za najnoviju verziju dodatka Maven Surefire, molimo provjerite ovdje.

Sada možete izvršiti exec: java Cilj je pokrenuti poslužitelj web usluga RESTful, a zatim pokrenuti gore navedene testove pomoću IDE-a. Jednako tako, test možete započeti izvršavanjem naredbe mvn -Dtest = Test servisnog testa u terminalu.

7. Zaključak

Ovaj je vodič ilustrirao upotrebu Apache CXF-a kao implementacije JAX-RS. Pokazalo je kako se okvir može koristiti za definiranje resursa za RESTful web uslugu i za stvaranje poslužitelja za objavljivanje usluge.

Provedbu svih ovih primjera i isječaka koda možete pronaći u projektu GitHub.


$config[zx-auto] not found$config[zx-overlay] not found