Architektury Usług Internetowych Laboratorium 2 RESTful Web Services Wstęp Celem laboratorium jest zapoznanie się z modelem usług sieciowych opartych na standardzie REST. RESTful Web Services Usługami sieciowymi nazywa się aplikacje klienckie i serwerowe komunikujące się ze sobą poprzez WWW z wykorzystaniem protokoły HTTP. Zakładają one interoperacyjność pomiędzy aplikacjami uruchomionymi na różnych platformach i frameworkach oraz opisy przetwarzane przez aplikacje (np. wykorzystujące XML). Luźno powiązane usługi można łączyć w celu uzyskania skomplikowanych operacji. Usługi oparte na standardzie REST (RESTful Web Services) powinny cechować się bezstanowością. Interakcja z serwerem powinna być odporna na jego restart a cała stanowość (ciąg kilku operacji) powinna być realizowana poprzez odpowiednie parametry w odnośnikach. Standard REST definiuje działanie na zasobach identyfikowanych poprzez URI poprzez operacje zdefiniowane w protokole HTTP: PUT stworzenie nowego zasobu, GET pobranie aktualnego stanu zasobu, POST aktualizacja stanu zasobu, DELETE usunięcie zasobu. Wykorzystanie URI do identyfikacji zasobów, metod HTTP do opisu operacji na nich oraz formatów XML i JSON do opisu danych przesyłanych pomiędzy klientem a serwerem sprawia że usługi typu REST są często używane w połączeniu z rozwiązaniami opartymi na języku JavaScript i technologii AJAX. Zgodnie ze standardem, zasoby powinny być ułożone w hierarchię. Przykładowo w sytuacji gdy podstawowym zasobem będzie książka zawierającego jakiegoś autora możemy założyć następujące adresy: resources/books lista wszystkich książek, resources/books/{id jedna książka o konkretnym id, resources/books/{id/author autor konkretnej książki. 2014 Michał Wójcik 1
W powyższym przykładzie {id jest parametrem typu path param, czyli będącym elementem adresu. Dodatkowo można używać parametrów typu query param, czyli takich, które są doklejane do adresu (po znaku?). Używa się ich raczej do dodatkowego sparametryzowania żądania, nie do identyfikacji zasobów na których działamy. Przykładowo żądanie listy książek z paginacją miało by następujący format: resources/books?offset=10&limit=5. Operacje na zasobach można odwzorować na CRUD, czyli podstawowe operacje wykorzystywane w aplikacjach działających na pamięci trwałej. Poniższa tabela pokazuje odwzorowanie podstawowej akcji na akcje w REST i SQL. Operacja SQL HTTP Create INSERT POST Read SELECT GET Update UPDATE PUT Delete DELETE DELETE Dla powyższych operacji można określić jakie wartości i kody HTTP powinny być zwracane w różnych sytuacjach. Poniższa tabela przedstawia takie zestawienie zarówno dla pobierania kolekcji elementów (np. resoruces/books) jak i pojedynczego konkretnego elementu (no. Resources/books/{id). Operacja Kolekcja Element GET 200 (OK), paginacja, sortowanie 200 (OK), pojedynczy element, 404 (Not Found) PUT 404 (Not Found) 200 (OK), 204 (No Content), 404 (Not Found) POST 201 (Created), Location w nagłówku 404 (Not Found) DELETE 404 (Not Found) 200 (OK), 404 (Not Found) JAX-RS Java API for RESTful Web Services JAX-RS jest standardem tworzenia usług sieciowych zgodnych ze standardem REST na platformie Java/Java EE. Standard jest obowiązkowy w ramach profili Java EE Web i Full profile, co oznacza że powinien być zaimplementowany w każdym serwerze aplikacji. Referencyjną implementacją standardu jest projekt Jersey, który jest wykorzystywany w serwerze aplikacji GlassFish a także może być uruchomiony w dowolnym kontenerze servlewów. Ponieważ serwer Apache Tomcat nie jest dostarczony z implementacją JAX- RS, należy samemu do projektu webowego dodać odpowiednią zależność, tak aby została wdrożona na serwer razem z aplikacją. <dependency> 2014 Michał Wójcik 2
<groupid>org.glassfish.jersey.containers</groupid> <artifactid>jersey container servlet</artifactid> <version>2.13</version> </dependency> <dependency> <groupid>org.glassfish.jersey.media</groupid> <artifactid>jersey media json jackson</artifactid> <version>2.13</version> </dependency> Biblioteka jerser-container-servlet zapewnia kontener dla usług sieciowych działający w kontenerze servletu, natomiast jersey-media-json-jackson zapewnia procesor formatu JSON. Jednym ze sposobów uruchomienia usług w aplikacji webowej jest skonfigurowanie centralnego servletu w pliku web.xml, który będzie przekierowywał żądania HTTP do odpowiednich metod skonfigurowanych jako usługi. <servlet> <servlet name>servletcontainer</servlet name> <servlet class> org.glassfish.jersey.servlet.servletcontainer </servlet class> <init param> <param name> jersey.config.server.provider.packages </param name> <param value> pl.gda.pg.eti.kask.aui.rest.resources </param value> </init param> <init param> <param name> jersey.config.server.provider.scanning.recursive </param ame> <param value>false</param value> </init param> </servlet> <servlet mapping> <servlet name>servletcontainer</servlet name> <url pattern>/resources/*</url pattern> </servlet mapping> Usługi oraz ich parametry można definiować za pomocą adnotacji: @Path adres zasobu, 2014 Michał Wójcik 3
żądania HTTP: @GET, @POST, @PUT, @DELETE, @HEAD; @PathParam parametr będący elementem URI, @QueryParam parametr doklejany do URI, @FormParam parametr wysłany przez formularz metodą POST, @DefaultValue domyślna wartość dla parametrów typu query param, @Consumes specyfikacja typu MIME przyjmowanego przez usługę, @Produces specyfikacja typu MIME zwracanego przez usługę, @Context pozwana na wstrzyknięcie obiektów: UriInfo zbiór wszystkich parametrów, HttpHeaders nagłówki i ciastka, ServletContext konteks servletu, HttpServletRequest obiek żądania, HttpServletResponse obiekt odpowiedzi. Przykład usługi zwracającej konkretną książkę w formacie JSON: @Path("/books") public class BooksResource { @Path("{id") @GET @Produces(MediaType.APPLICATION_JSON) public Book findbook(@pathparam("id") Integer id) {... Przykład usługi zwracającej listę książek w formacje JSON z obsługą paginacji: 2014 Michał Wójcik 4
@Path("/books") public class BooksResource { @Path("") @GET @Produces(MediaType.APPLICATION_JSON) public List<Book> findbooks( @DefaultValue("0") @QueryParam("off") Integer off, @DefaultValue("10") @QueryParam("size") Integer size) {... JAX-RS klient Aby przygotować aplikację Java SE będącą klientem dla usługi typu rest można skorzystać z bibliotek dostarczonych przez projekt Jersey. <dependency> <groupid>org.glassfish.jersey.core</groupid> <artifactid>jersey client</artifactid> <version>2.13</version> </dependency> <dependency> <groupid>org.glassfish.jersey.media</groupid> <artifactid>jersey media json jackson</artifactid> <version>2.13</version> </dependency> W celu nawiązania połączenia z serwerem należy stworzyć obiekt Client, następnie z jego pomocą stworzyć obiekt WebTarget, który będzie służył wywoływaniu kolejnych żądań. Client client = ClientBuilder.newClient(); WebTarget root = client.target(address + "/resources/books"); List<Book> books = root.path("").request(mediatype.application_json_type).get(new GenericType<List<Book>>() {); Book book = root.path(1).request().get(book.class); Zabezpieczenie usług Jednym ze sposobów zabezpieczania usług jest zabezpieczenie na poziomie kontenera webowego. Serwer Apache Tomcat pozwala na definiowanie domen bezpieczeństwa opartych różnych mechanizmach (pliki xml, bazy danych, ldap itd), ale dla uproszczenia można skorzystać z domyślnego pliku conf/tomcatusers.xml: 2014 Michał Wójcik 5
<?xml version="1.0" encoding="utf 8"?> <tomcat users> <user password="nb" roles="manager script,admin" username="nb"/> <user password="psysiu" roles="reader" username="psysiu"/> <user password="admin" roles="reader,editor" username="admin"/> </tomcat users> Następnie należy zdefiniować role użytkowników w deskryptorze aplikacji webowej WEB-INF/web.xml: <web app> <security role> <role name>editor</role name> </security role> <security role> <role name>reader</role name> </security role> </web app> Aby umożliwić logowanie z wykorzystaniem metody BASIC AUTHENTICATION (pozwalającej na wysłanie loginu i hasła w nagłówku HTTP) należy dodać odpowiedni wpis w deskryptorze web.xml. <web app> <login config> <auth method>basic</auth method> </login config> </web app> Ostatnim elementem w deskryptorze web.xml jest zdefiniowanie które zasoby i strony wymagają uwierzytelniania i autoryzacji: <web app> <security constraint> <web resource collection> <web resource name>management pages</web resource name> <url pattern>/list_books.html</url pattern> <url pattern>/resources/*</url pattern> </web resource collection> <auth constraint> <role name>editor</role name> <role name>reader</role name> </auth constraint> </security constraint> </web app> Z poziomu kodu usługi autoryzację można zrealizować za pomocą obiektu typu SecuriyConext uzsykanego za pomocą adnotacji @Context. 2014 Michał Wójcik 6
@Path("/books") public class BooksResource { @Context SecurityContext securitycontext; @Path("") @GET @Produces(MediaType.APPLICATION_JSON) public List<Book> findbooks( @DefaultValue("0") @QueryParam("off") Integer off, @DefaultValue("10") @QueryParam("size") Integer size) { if (securitycontext.isuserinrole("reader")) {... else {... Aby umożliwić przekazanie loginu i hasła w nagłówkach HTTP z poziomu klienta napisanego w Java SE należy skorzystać z klasy HttpAuthenticationFeature: Client client = ClientBuilder.newClient(); HttpAuthenticationFeature feature; feature = HttpAuthenticationFeature.basic(login, password); client.register(feature); Elementy przykładu Dla usług typu REST przygotowano trzy przykłady. Wszystkie bazują na przykładzie z książkami opartym na servletach, więc po dokładniejszy opis należy udać się do instrukcji dla zadania pierwszego. Przykłady: SimpleRest servlety odpowiedzialne za zarządzanie książkami zamieniono na jedną klasę udostępniającą usługi, Rest rozwinięcie poprzedniego przykładu o dodatkowe elementy oferowane przez technologię JAX-RS, SecuredRest uproszony przykład wykorzystujący autoryzacja i uwierzytelnianie, RestClient klient usług wykonany w technologii Java SE pozwalający na przekazanie loginu i hasła do usługi. Elementy projektu RestClient: 2014 Michał Wójcik 7
pl.gda.pg.eti.kask.aui.rest: entities: Book.java klasa reprezentującą książkę; client: App.java główna klasa programu, BookResource.java klasa pozwalająca na wywoływanie usług, zawiera te same metody co klasa BookResource.java w projektach webowych. Przykładowe zadania W ramach laboratorium należy skonfigurować środowisko (Apache Tomcat) oraz uruchomić na nim przykładową aplikację (jedna z trzech, wybrana przez prowadzącego). Następnie należy wprowadzić modyfikację wybraną przez prowadzącego. Modyfikacje mogą dotyczyć dodania obsługi nowej komendy do usługi (wraz odpowiednimi wywołaniami z poziomu JavaScript lub HTML), zmiany lub dodanie obiektów przechowywanych w zasięgu sesji lub aplikacji oraz zmiany istniejących funkcjonalności usług (np. przez zmianę parametrów przez nie obsługiwanych). Po oddaniu zadania należy usunąć wszystkie swoje zmiany (ręcznie zainstalowany server Apache Tomcat lub stworzony prywatny katalog instancji serwera oraz projekt). 2014 Michał Wójcik 8