Difference between revisions of "Jax-rs 2.0"
(→web.xml) |
(→Service osztály definiálása) |
||
Line 135: | Line 135: | ||
==Service osztály definiálása== | ==Service osztály definiálása== | ||
+ | A service osztályokat nagyon egyszerű definiálni, az alábbi osztály loginByEmail metódusa a POST:<app root>/rest/login/loginbyemail URL-en lesz elérhető. A HTTP body-ban a LoginByEmalLoginHelperRequest osztály JSON reprezentációját fogja várni, amit automatikusan mappelni fog a JAX-RS egy LoginByEmalLoginHelperRequest objektum példányba. A metódus a TokenResponse típusú objektummal tér vissza, mait a JAX-RS automatikusan JSON -ra fog mappelni anélkül, hogy vagy a request vagy a response objektumba bármilyen annotációt el kéne helyezni. Bonyolult, vagy nem egyértelmű struktúrák esetén szükség lehet rá, hogy annotációkkal támogassuk a JSON<->JAVA mapper-t, lásd lentebb. | ||
+ | <source lang="java"> | ||
+ | import javax.ws.rs.POST; | ||
+ | import javax.ws.rs.Path; | ||
+ | |||
+ | |||
+ | @Path("/login/") | ||
+ | @Consumes({ "application/json" }) | ||
+ | @Produces({ "application/json" }) | ||
+ | public class LoginService { | ||
+ | @POST | ||
+ | @Path("/loginbyemail") | ||
+ | public TokenResponse loginByEmail(LoginByEmalLoginHelperRequest loginByEmalRequest) throws WebServiceException { | ||
+ | |||
+ | return new TokenResponse(); | ||
+ | } | ||
+ | @GET | ||
+ | @Path("/book/{isbn}") | ||
+ | public Book getBook(@PathParam("isbn") String id, @QueryParam("title") String title) { | ||
+ | Book oneBook = new Book(); | ||
+ | oneBook.setIsbn("ISBN11111"); | ||
+ | oneBook.setTitle("Mastering Jax-RS"); | ||
− | + | return oneBook; | |
− | |||
− | + | } | |
− | |||
+ | } | ||
</source> | </source> | ||
+ | A második metódus (getBook) a ''GET:<app root>/rest/book/'' URL-en érhető el. Vár egy PATH paramétert a /book/ után, valamint egy title nevű QUERY paramétert, tehát a teljes request valami ilyesmi lesz: ''GET:<app root>/rest/book/12345?title=adam'' | ||
+ | |||
+ | ==Hibakezelés== | ||
+ | Ez egy sarkalatos pontja a REST interfész implementációnak. A @GET/POST/PUT annotációkkal ellátott metódusoknál csak azt tudjuk definiálni, hogy mi legyen a válasz típusa happy ágon, mikor a kérést rendeltetés szerűen ki tudta szolgálni a szerver, tehát a HTTP státusz kódja a válasznak a 200-as tartományba fog esni. Ha van visszatérési érték, akkor feltehetően ez 200 lesz, ha nincs, akkor 204, ezt automatikusan kezeli a JAX-RS. | ||
+ | |||
+ | |||
+ | Viszont mi van akkor ha hiba történik a feldolgozásban. A kliensnek semmilyen körülmények közözz nem szabad egy java stack trace-t visszaadni, minden körülmények között egy szabályos REST választ kell visszaküldeni, ahol a HTTP státusz megfelelően ki van töltve, és lehetőleg egy egységesített ERROR objektumban benne van a hiba leírása. | ||
+ | * 400-as hibák: A feldogozás az input adatok miatt nem sikerült, pl hiányzik valami, vagy nincs joga a kliensek a kérést végrehajtania | ||
+ | * 500-as hibák: Az input paraméterek megfelelőek voltak, de a szerver oldalon nem várt hiba történt. (pl nem tudtunk egy szükséges háttérrendszerhez csatlakozni) | ||
+ | |||
+ | |||
+ | Ahhoz hogy a hibaágakat is kezelni tudjuk, létre kell hozzunk egyedi Exception osztályokat, és hozzájuk tartozó response mapper-eket, amik az Exception eldobása estén megkapják a vezérlést, és az Exception alapján össze tudják állítani a választ. Ehhez három osztályt kell létrehozzunk: | ||
+ | # ErrorResponse osztály, egy egységesített struktúra, amit minden nem 200-as válaszban vár a kliens. (pl hibakódok, szöveges leírás) | ||
+ | # Saját Exception implementáció (WebServiceException), amibe minden olyan infónak szerepelnie kell, ami alapján az ErrorResponse osztály elkészíthető. | ||
+ | # Exception mapper osztály, ami megkapja a vezérlést ha WebServiceException dobódott, és a benne található információk alapján példányosítja a ErrorResponse osztályt, majd beleteszi a REST válaszba, amit a JAX-RS JSON formátumra fog hozni. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | <source lang="java"> | ||
+ | import javax.ws.rs.core.MediaType; | ||
+ | import javax.ws.rs.core.Response; | ||
+ | import javax.ws.rs.ext.ExceptionMapper; | ||
+ | import javax.ws.rs.ext.Provider; | ||
+ | import hu.adam.loginHelper.exception.WebServiceException; | ||
+ | |||
+ | @Provider | ||
+ | public class WebServiceExceptionMapper implements ExceptionMapper<WebServiceException> { | ||
+ | @Override | ||
+ | public Response toResponse(WebServiceException exception) { | ||
+ | |||
+ | return Response.status(exception.getStatus()) | ||
+ | .entity(exception.getErrorMessageResponse()) | ||
+ | .type(MediaType.APPLICATION_JSON). | ||
+ | build(); | ||
+ | } | ||
+ | } | ||
=JAX-RS kliens= | =JAX-RS kliens= |
Revision as of 12:39, 16 February 2019
Contents
JAX-RS szerver
Inicializálás
A JAX-RS szerver futtatásához el kell indítsuk a Jersy servlet-et a WEB alkalmazásunkban, ami kiválóan megfér más servlet-ek mellett, pl JSF. A Jersey servletnek meg fogjuk mondani, hogy milyen PATH tartozik hozzá, így minden más erőforrás kérés továbbra is a JSF servlet-hez fog beesni.
Maven dependenciák
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>2.28</version>
</dependency>
web.xml
Ha JSF-et akarunk párhuzamosan JAX-RS-el használni, akkor nincs más dolgunk, mint hogy mint két serlvet implementációt felvegyünk a web.xml-be. Elsőként a JSF implementációt, ami az esetünkben PrimeFaces lesz, aztán meg felvesszük a JAX-RS serlvetet, ami az esetünkben Glassfish-Jeresy lesz.
A Glassfish-Jeresy-t többféle képen lehet paraméterezni. Vagy itt, a web.xml-ben adjuk meg a szükséges paramétereket (provide-erek, mapperek, stb..) vagy implementáljuk a javax.ws.rs.core.Application osztályt, megadjuk a helyét a javax.ws.rs.Application paraméterrel, majd a konfigurációt többi részét az osztályon belül definiáljuk.
Elsőre nézzük az a web.xml-es konfigurációt. <init-param> szekciókkal kell megadni a Jersey paramétereit:
- jersey.config.server.provider.packages: meg kell adni azt a java csomagot, ahol a webservice implementációk és az exception mapper-ek vannak. Mi itt azt a csomagot adjuk meg, ahol a service implementációk vannak.
- jersey.config.server.provider.classnames: fel lehet sorolni konkrét class megadásokkal további implementációkat. Mi itt az Exception mapper-eket adjuk meg.
A servlet-mapping szekcióban elsőre megadjuk hogy a Jeresey servlet a /rest/ útvonalon fog hallgatózni. Aztán adjuk meg a JSF servletet, ami minden másra illeszkedni fog.
...
<!-- JSF servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Jersey servlet -->
<servlet>
<servlet-name>Jersey Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>hu.adam.loginHelper.jaxrs.service</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
hu.adam.loginHelper.jaxrs.mapper.AppExceptionMapper;
hu.adam.loginHelper.jaxrs.mapper.ErrorMessage;
hu.adam.loginHelper.jaxrs.mapper.WebServiceExceptionMapper
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Jersey mapping -->
<servlet-mapping>
<servlet-name>Jersey Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<!-- JSF servlet -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
Másik alternatíva lenne ha JAX-RS konfigurációt a javax.ws.rs.core.Application osztály implementációjában definiálnánk. Ekkor a web.xml-ben csak az implementációs osztályt kell megadni: ha a web.xml-ben az
<!-- Jersey servlet -->
<servlet>
<servlet-name>Jersey Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>hu.adam.MyApplication</param-value>
</init-param>
Majd a tovább konfigurációkat az implementációs osztályban definiáljuk:
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/rest/")
public class MyApplication extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
//Webservices
s.add(LoginService.class);
//Mappers
s.add(AppExceptionMapper.class);
s.add(GenericExceptionMapper.class);
return s;
}
}
Service osztály definiálása
A service osztályokat nagyon egyszerű definiálni, az alábbi osztály loginByEmail metódusa a POST:<app root>/rest/login/loginbyemail URL-en lesz elérhető. A HTTP body-ban a LoginByEmalLoginHelperRequest osztály JSON reprezentációját fogja várni, amit automatikusan mappelni fog a JAX-RS egy LoginByEmalLoginHelperRequest objektum példányba. A metódus a TokenResponse típusú objektummal tér vissza, mait a JAX-RS automatikusan JSON -ra fog mappelni anélkül, hogy vagy a request vagy a response objektumba bármilyen annotációt el kéne helyezni. Bonyolult, vagy nem egyértelmű struktúrák esetén szükség lehet rá, hogy annotációkkal támogassuk a JSON<->JAVA mapper-t, lásd lentebb.
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@Path("/login/")
@Consumes({ "application/json" })
@Produces({ "application/json" })
public class LoginService {
@POST
@Path("/loginbyemail")
public TokenResponse loginByEmail(LoginByEmalLoginHelperRequest loginByEmalRequest) throws WebServiceException {
return new TokenResponse();
}
@GET
@Path("/book/{isbn}")
public Book getBook(@PathParam("isbn") String id, @QueryParam("title") String title) {
Book oneBook = new Book();
oneBook.setIsbn("ISBN11111");
oneBook.setTitle("Mastering Jax-RS");
return oneBook;
}
}
A második metódus (getBook) a GET:<app root>/rest/book/ URL-en érhető el. Vár egy PATH paramétert a /book/ után, valamint egy title nevű QUERY paramétert, tehát a teljes request valami ilyesmi lesz: GET:<app root>/rest/book/12345?title=adam
Hibakezelés
Ez egy sarkalatos pontja a REST interfész implementációnak. A @GET/POST/PUT annotációkkal ellátott metódusoknál csak azt tudjuk definiálni, hogy mi legyen a válasz típusa happy ágon, mikor a kérést rendeltetés szerűen ki tudta szolgálni a szerver, tehát a HTTP státusz kódja a válasznak a 200-as tartományba fog esni. Ha van visszatérési érték, akkor feltehetően ez 200 lesz, ha nincs, akkor 204, ezt automatikusan kezeli a JAX-RS.
Viszont mi van akkor ha hiba történik a feldolgozásban. A kliensnek semmilyen körülmények közözz nem szabad egy java stack trace-t visszaadni, minden körülmények között egy szabályos REST választ kell visszaküldeni, ahol a HTTP státusz megfelelően ki van töltve, és lehetőleg egy egységesített ERROR objektumban benne van a hiba leírása.
- 400-as hibák: A feldogozás az input adatok miatt nem sikerült, pl hiányzik valami, vagy nincs joga a kliensek a kérést végrehajtania
- 500-as hibák: Az input paraméterek megfelelőek voltak, de a szerver oldalon nem várt hiba történt. (pl nem tudtunk egy szükséges háttérrendszerhez csatlakozni)
Ahhoz hogy a hibaágakat is kezelni tudjuk, létre kell hozzunk egyedi Exception osztályokat, és hozzájuk tartozó response mapper-eket, amik az Exception eldobása estén megkapják a vezérlést, és az Exception alapján össze tudják állítani a választ. Ehhez három osztályt kell létrehozzunk:
- ErrorResponse osztály, egy egységesített struktúra, amit minden nem 200-as válaszban vár a kliens. (pl hibakódok, szöveges leírás)
- Saját Exception implementáció (WebServiceException), amibe minden olyan infónak szerepelnie kell, ami alapján az ErrorResponse osztály elkészíthető.
- Exception mapper osztály, ami megkapja a vezérlést ha WebServiceException dobódott, és a benne található információk alapján példányosítja a ErrorResponse osztályt, majd beleteszi a REST válaszba, amit a JAX-RS JSON formátumra fog hozni.
<source lang="java"> import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import hu.adam.loginHelper.exception.WebServiceException;
@Provider public class WebServiceExceptionMapper implements ExceptionMapper<WebServiceException> { @Override public Response toResponse(WebServiceException exception) {
return Response.status(exception.getStatus()) .entity(exception.getErrorMessageResponse()) .type(MediaType.APPLICATION_JSON). build(); } }