Usługi sieciowe RESTful - szybki przewodnik
Co to jest architektura REST?
REST oznacza REpresentational State Transfer. REST to architektura oparta na standardach internetowych i wykorzystuje protokół HTTP. Obraca się wokół zasobu, w którym każdy komponent jest zasobem, a dostęp do zasobu uzyskuje się przez wspólny interfejs przy użyciu standardowych metod HTTP. REST został po raz pierwszy wprowadzony przez Roya Fieldinga w 2000 roku.
W architekturze REST serwer REST po prostu zapewnia dostęp do zasobów, a klient REST uzyskuje dostęp i modyfikuje zasoby. Tutaj każdy zasób jest identyfikowany przez URI / globalne ID. REST używa różnych reprezentacji do reprezentowania zasobów, takich jak tekst, JSON, XML. JSON jest najpopularniejszym.
Metody HTTP
Następujące cztery metody HTTP są powszechnie używane w architekturze opartej na REST.
GET - Zapewnia dostęp tylko do odczytu do zasobu.
POST - Służy do tworzenia nowego zasobu.
DELETE - Służy do usuwania zasobu.
PUT - Służy do aktualizowania istniejącego zasobu lub tworzenia nowego zasobu.
Wprowadzenie do usług sieciowych RESTFul
Usługa internetowa to zbiór otwartych protokołów i standardów używanych do wymiany danych między aplikacjami lub systemami. Aplikacje napisane w różnych językach programowania i działające na różnych platformach mogą wykorzystywać usługi sieciowe do wymiany danych w sieciach komputerowych, takich jak Internet, w sposób podobny do komunikacji między procesami na jednym komputerze. Ta interoperacyjność (np. Między aplikacjami Java i Python lub Windows i Linux) wynika z wykorzystania otwartych standardów.
Usługi sieciowe oparte na architekturze REST są nazywane usługami sieciowymi RESTful. Te usługi sieciowe wykorzystują metody HTTP do implementacji koncepcji architektury REST. Usługa sieciowa zgodna z REST zwykle definiuje identyfikator URI, Uniform Resource Identifier a usługę, zapewnia reprezentację zasobów, takich jak JSON i zestaw metod HTTP.
Tworzenie RESTFul Webservice
W następnych rozdziałach utworzymy usługę sieciową, mówiącą o zarządzaniu użytkownikami z następującymi funkcjami -
Sr.No. | URI | Metoda HTTP | POST body | Wynik |
---|---|---|---|---|
1 | / UserService / users | DOSTAĆ | pusty | Pokaż listę wszystkich użytkowników. |
2 | / UserService / addUser | POCZTA | Ciąg JSON | Dodaj szczegóły nowego użytkownika. |
3 | / UserService / getUser /: id | DOSTAĆ | pusty | Pokaż szczegóły użytkownika. |
W tym samouczku dowiesz się, jak przygotować środowisko programistyczne do rozpoczęcia pracy Jersey Frameworkdo tworzenia RESTful Web Services. Narzędzia szkieletowe z JerseyJAX-RS 2.0API, które jest standardową specyfikacją do tworzenia usług WWW zgodnych z REST. Ten samouczek nauczy Cię również, jak skonfigurowaćJDK, Tomcat i Eclipse na twoim komputerze, zanim skonfigurujesz Jersey Framework.
Skonfiguruj zestaw Java Development Kit (JDK)
Najnowszą wersję pakietu SDK można pobrać z witryny Oracle Java - Java SE Downloads . W pobranych plikach znajdziesz instrukcje dotyczące instalacji JDK. Postępuj zgodnie z podanymi instrukcjami, aby zainstalować i skonfigurować instalację. Wreszcie ustawPATH i JAVA_HOME zmienne środowiskowe odnoszące się do katalogu, który zawiera Java i Javac, zwykle odpowiednio katalog_instalacyjny_java / bin i katalog_instalacyjny_java.
Jeśli używasz systemu Windows i zainstalowałeś JDK w C: \ jdk1.7.0_75, musisz umieścić następujący wiersz w pliku C: \ autoexec.bat.
set PATH = C:\jdk1.7.0_75\bin;%PATH%
set JAVA_HOME = C:\jdk1.7.0_75
Alternatywnie, w Windows NT / 2000 / XP, możesz także kliknąć prawym przyciskiem Mój komputer → wybierz Właściwości → następnie Zaawansowane → następnie Zmienne środowiskowe. Następnie zaktualizuj wartość PATH i naciśnij przycisk OK.
W systemie Unix (Solaris, Linux itp.), Jeśli SDK jest zainstalowany w /usr/local/jdk1.7.0_75 i używasz powłoki C, w pliku .cshrc umieścisz następujące elementy.
setenv PATH /usr/local/jdk1.7.0_75/bin:$PATH
setenv JAVA_HOME /usr/local/jdk1.7.0_75
Alternatywnie, jeśli używasz zintegrowanego środowiska programistycznego (IDE), takiego jak Borland JBuilder, Eclipse, IntelliJ IDEA lub Sun ONE Studio, skompiluj i uruchom prosty program, aby potwierdzić, że IDE wie, gdzie zainstalowałeś Javę, w przeciwnym razie wykonaj odpowiednią konfigurację zgodnie z podanym dokumentem IDE.
Skonfiguruj Eclipse IDE
Wszystkie przykłady w tym samouczku zostały napisane przy użyciu środowiska Eclipse IDE. Sugerowałbym więc, abyś miał zainstalowaną najnowszą wersję Eclipse na swoim komputerze.
Aby zainstalować Eclipse IDE, pobierz najnowsze pliki binarne Eclipse z https://www.eclipse.org/downloads/. Po pobraniu instalacji rozpakuj dystrybucję binarną w dogodnym miejscu. Na przykład w C: \ eclipse w systemie Windows lub / usr / local / eclipse w systemie Linux / Unix i na koniec ustaw odpowiednio zmienną PATH.
Eclipse można uruchomić, wykonując następujące polecenia na komputerze z systemem Windows lub klikając dwukrotnie plik eclipse.exe.
%C:\eclipse\eclipse.exe
Eclipse można uruchomić, wykonując następujące polecenia na komputerze z systemem Unix (Solaris, Linux itp.) -
$/usr/local/eclipse/eclipse
Po udanym uruchomieniu, jeśli wszystko jest w porządku, na ekranie powinien wyświetlić się następujący wynik -
Skonfiguruj biblioteki Jersey Framework
Teraz, jeśli wszystko jest w porządku, możesz przystąpić do konfiguracji frameworka Jersey. Poniżej znajduje się kilka prostych kroków, aby pobrać i zainstalować framework na swoim komputerze.
Wybierz, czy chcesz zainstalować Jersey w systemie Windows, czy Unix, a następnie przejdź do następnego kroku, aby pobrać plik .zip dla systemu Windows, a następnie plik .tz dla systemu Unix.
Pobierz najnowszą wersję plików binarnych platformy Jersey z poniższego linku - https://jersey.java.net/download.html.
W momencie pisania tego samouczka pobrałem jaxrs-ri-2.17.zip na moim komputerze z systemem Windows i po rozpakowaniu pobranego pliku da ci strukturę katalogów wewnątrz E: \ jaxrs-ri-2.17 \ jaxrs-ri, jak pokazano na poniższym zrzucie ekranu.
W katalogach znajdziesz wszystkie biblioteki Jersey C:\jaxrs-ri-2.17\jaxrs-ri\lib i zależności w C:\jaxrs-ri-2.17\jaxrs-ri\ext. Upewnij się, że ustawiłeś zmienną CLASSPATH w tym katalogu poprawnie, w przeciwnym razie napotkasz problem podczas uruchamiania aplikacji. Jeśli używasz Eclipse, nie jest wymagane ustawianie CLASSPATH, ponieważ wszystkie ustawienia będą wykonywane przez Eclipse.
Skonfiguruj Apache Tomcat
Najnowszą wersję programu Tomcat można pobrać z witryny https://tomcat.apache.org/. Po pobraniu instalacji rozpakuj dystrybucję binarną w dogodnej lokalizacji. Na przykład w C: \ apache-tomcat-7.0.59 w systemie Windows lub /usr/local/apache-tomcat-7.0.59 w systemie Linux / Unix i ustaw zmienną środowiskową CATALINA_HOME wskazującą miejsca instalacji.
Tomcat można uruchomić, wykonując następujące polecenia na komputerze z systemem Windows lub po prostu klikając dwukrotnie plik startup.bat.
%CATALINA_HOME%\bin\startup.bat
lub
C:\apache-tomcat-7.0.59\bin\startup.bat
Tomcat można uruchomić, wykonując następujące polecenia na komputerze z systemem Unix (Solaris, Linux itp.) -
$CATALINA_HOME/bin/startup.sh
lub
/usr/local/apache-tomcat-7.0.59/bin/startup.sh
Po pomyślnym uruchomieniu domyślne aplikacje internetowe dołączone do Tomcat będą dostępne pod adresem http://localhost:8080/. Jeśli wszystko jest w porządku, powinien wyświetlić następujący wynik -
Więcej informacji na temat konfigurowania i uruchamiania Tomcat można znaleźć w dokumentacji zawartej na tej stronie. Te informacje można również znaleźć na stronie internetowej Tomcat -https://tomcat.apache.org.
Tomcat można zatrzymać, wykonując następujące polecenia na komputerze z systemem Windows -
%CATALINA_HOME%\bin\shutdown
lub
C:\apache-tomcat-7.0.59\bin\shutdown
Tomcat można zatrzymać, wykonując następujące polecenia na komputerze z systemem Unix (Solaris, Linux itp.) -
$CATALINA_HOME/bin/shutdown.sh
lub
/usr/local/apache-tomcat-7.0.59/bin/shutdown.sh
Gdy skończysz z ostatnim krokiem, możesz przejść do pierwszego przykładu z Jersey, który zobaczysz w następnym rozdziale.
Zacznijmy pisać rzeczywiste usługi internetowe RESTful z Jersey Framework. Zanim zaczniesz pisać swój pierwszy przykład przy użyciu Jersey Framework, musisz upewnić się, że środowisko Jersey zostało poprawnie skonfigurowane, jak wyjaśniono w rozdziale Usługi sieciowe RESTful - Konfiguracja środowiska . Tutaj zakładam również, że masz trochę praktycznej wiedzy na temat Eclipse IDE.
Więc przejdźmy do napisania prostej aplikacji Jersey, która ujawni metodę usługi sieciowej w celu wyświetlenia listy użytkowników.
Tworzenie projektu Java
Pierwszym krokiem jest utworzenie dynamicznego projektu internetowego przy użyciu środowiska Eclipse IDE. Postępuj zgodnie z opcjąFile → New → Project i na koniec wybierz Dynamic Web Projectkreator z listy kreatorów. Teraz nazwij swój projekt jakoUserManagement używając okna kreatora, jak pokazano na poniższym zrzucie ekranu -
Po pomyślnym utworzeniu projektu będziesz mieć następującą zawartość w swoim Project Explorer -
Dodawanie wymaganych bibliotek
W drugim kroku dodajmy Jersey Framework i jego zależności (biblioteki) do naszego projektu. Skopiuj wszystkie słoiki z następujących katalogów folderu zip z pobraną koszulką w katalogu WEB-INF / lib projektu.
- \jaxrs-ri-2.17\jaxrs-ri\api
- \jaxrs-ri-2.17\jaxrs-ri\ext
- \jaxrs-ri-2.17\jaxrs-ri\lib
Teraz kliknij prawym przyciskiem myszy nazwę swojego projektu UserManagement a następnie skorzystaj z opcji dostępnej w menu kontekstowym - Build Path → Configure Build Path aby wyświetlić okno ścieżki budowania języka Java.
Teraz użyj Add JARs przycisk dostępny pod Libraries aby dodać pliki JAR obecne w katalogu WEBINF / lib.
Tworzenie plików źródłowych
Teraz stwórzmy rzeczywiste pliki źródłowe w ramach UserManagementprojekt. Najpierw musimy utworzyć pakiet o nazwiecom.tutorialspoint. Aby to zrobić, kliknij prawym przyciskiem myszy src w sekcji eksploratora pakietów i postępuj zgodnie z opcją -New → Package.
Następnie stworzymy UserService.java, User.java,UserDao.java pliki w pakiecie com.tutorialspoint.
User.java
package com.tutorialspoint;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String profession;
public User(){}
public User(int id, String name, String profession){
this.id = id;
this.name = name;
this.profession = profession;
}
public int getId() {
return id;
}
@XmlElement
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
@XmlElement
public void setName(String name) {
this.name = name;
}
public String getProfession() {
return profession;
}
@XmlElement
public void setProfession(String profession) {
this.profession = profession;
}
}
UserDao.java
package com.tutorialspoint;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class UserDao {
public List<User> getAllUsers(){
List<User> userList = null;
try {
File file = new File("Users.dat");
if (!file.exists()) {
User user = new User(1, "Mahesh", "Teacher");
userList = new ArrayList<User>();
userList.add(user);
saveUserList(userList);
}
else{
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
userList = (List<User>) ois.readObject();
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return userList;
}
private void saveUserList(List<User> userList){
try {
File file = new File("Users.dat");
FileOutputStream fos;
fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(userList);
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UserService.java
package com.tutorialspoint;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/UserService")
public class UserService {
UserDao userDao = new UserDao();
@GET
@Path("/users")
@Produces(MediaType.APPLICATION_XML)
public List<User> getUsers(){
return userDao.getAllUsers();
}
}
Należy zwrócić uwagę na dwie ważne kwestie dotyczące programu głównego,
UserService.java
Pierwszym krokiem jest określenie ścieżki do usługi internetowej przy użyciu adnotacji @Path do usługi UserService.
Drugim krokiem jest określenie ścieżki dla określonej metody usługi sieciowej za pomocą adnotacji @Path do metody UserService.
Tworzenie pliku konfiguracyjnego Web.xml
Musisz utworzyć plik konfiguracyjny Web xml, który jest plikiem XML i służy do określenia serwletu struktury Jersey dla naszej aplikacji.
web.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns = "http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id = "WebApp_ID" version = "3.0">
<display-name>User Management</display-name>
<servlet>
<servlet-name>Jersey RESTful Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.tutorialspoint</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Jersey RESTful Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
Wdrażanie programu
Gdy skończysz tworzyć pliki konfiguracyjne źródła i sieci, możesz przystąpić do tego kroku, jakim jest kompilacja i uruchomienie programu. Aby to zrobić, za pomocą Eclipse wyeksportuj aplikację jako plik wojenny i wdróż to samo w tomcat.
Aby utworzyć plik WAR za pomocą eclipse, postępuj zgodnie z opcją File → export → Web → War Filei na koniec wybierz Zarządzanie użytkownikami projektu i folder docelowy. Aby wdrożyć plik wojny w Tomcat, umieść plik UserManagement.war w plikuTomcat Installation Directory → webapps directory i uruchom Tomcat.
Uruchomienie programu
Do testowania naszych usług internetowych używamy Postman , rozszerzenia przeglądarki Chrome.
Poproś o zarządzanie użytkownikami, aby uzyskać listę wszystkich użytkowników. Umieść http: // localhost: 8080 / UserManagement / rest / UserService / users w POSTMAN z żądaniem GET i zobacz następujący wynik.
Gratulacje, pomyślnie utworzyłeś swoją pierwszą aplikację RESTful.
Co to jest zasób?
Architektura REST traktuje każdą zawartość jako zasób. Te zasoby mogą być plikami tekstowymi, stronami HTML, obrazami, filmami lub dynamicznymi danymi biznesowymi. Serwer REST po prostu zapewnia dostęp do zasobów, a klient REST uzyskuje dostęp i modyfikuje zasoby. Tutaj każdy zasób jest identyfikowany przez URI / Globalne ID. REST używa różnych reprezentacji do reprezentowania zasobu, w którym Text, JSON, XML. Najpopularniejsze reprezentacje zasobów to XML i JSON.
Reprezentacja zasobów
Zasób w REST jest podobnym obiektem w programowaniu obiektowym lub jest podobny do jednostki w bazie danych. Po zidentyfikowaniu zasobu należy zdecydować o jego reprezentacji przy użyciu standardowego formatu, tak aby serwer mógł wysłać zasób w powyższym formacie, a klient mógł zrozumieć ten sam format.
Na przykład w rozdziale RESTful Web Services - First Application użytkownik jest zasobem reprezentowanym przy użyciu następującego formatu XML -
<user>
<id>1</id>
<name>Mahesh</name>
<profession>Teacher</profession>
</user>
Ten sam zasób można przedstawić w formacie JSON w następujący sposób -
{
"id":1,
"name":"Mahesh",
"profession":"Teacher"
}
Dobra reprezentacja zasobów
REST nie nakłada żadnych ograniczeń na format reprezentacji zasobów. Klient może poprosić o reprezentację JSON, podczas gdy inny klient może poprosić o reprezentację XML tego samego zasobu na serwerze i tak dalej. Za przekazanie klientowi zasobu w formacie zrozumiałym dla klienta odpowiada serwer REST.
Poniżej przedstawiono kilka ważnych punktów, które należy wziąć pod uwagę podczas projektowania formatu reprezentacji zasobu w usługach sieci Web zgodnych z REST.
Understandability - Zarówno serwer, jak i klient powinni być w stanie zrozumieć i wykorzystać format reprezentacji zasobu.
Completeness- Format powinien być w stanie w pełni reprezentować zasób. Na przykład zasób może zawierać inny zasób. Format powinien być w stanie przedstawić zarówno proste, jak i złożone struktury zasobów.
Linkablity - Zasób może mieć łącze do innego zasobu, format powinien być w stanie obsłużyć takie sytuacje.
Jednak obecnie większość usług internetowych przedstawia zasoby przy użyciu formatu XML lub JSON. Dostępnych jest wiele bibliotek i narzędzi do zrozumienia, analizowania i modyfikowania danych XML i JSON.
Usługi RESTful Web Services wykorzystują protokoły HTTP jako środek komunikacji między klientem a serwerem. Klient wysyła wiadomość w postaci żądania HTTP, a serwer odpowiada w postaci odpowiedzi HTTP. Ta technika jest określana jako Messaging. Wiadomości te zawierają dane i metadane wiadomości, tj. Informacje o samej wiadomości. Przyjrzyjmy się komunikatom żądania HTTP i odpowiedzi HTTP dla protokołu HTTP 1.1.
Żądanie HTTP
Żądanie HTTP składa się z pięciu głównych części -
Verb - Wskazuje metody HTTP, takie jak GET, POST, DELETE, PUT itp.
URI - Uniform Resource Identifier (URI) do identyfikacji zasobu na serwerze.
HTTP Version- Wskazuje wersję HTTP. Na przykład HTTP v1.1.
Request Header- Zawiera metadane komunikatu żądania HTTP w postaci par klucz-wartość. Na przykład typ klienta (lub przeglądarki), format obsługiwany przez klienta, format treści wiadomości, ustawienia pamięci podręcznej itp.
Request Body - Treść wiadomości lub reprezentacja zasobów.
Odpowiedź HTTP
Odpowiedź HTTP składa się z czterech głównych części -
Status/Response Code- Wskazuje stan serwera dla żądanego zasobu. Na przykład 404 oznacza, że nie znaleziono zasobu, a 200 oznacza, że odpowiedź jest w porządku.
HTTP Version- Wskazuje wersję HTTP. Na przykład HTTP v1.1.
Response Header- Zawiera metadane komunikatu odpowiedzi HTTP w postaci par klucz-wartość. Na przykład długość treści, typ treści, data odpowiedzi, typ serwera itp.
Response Body - Treść wiadomości z odpowiedzią lub reprezentacja zasobów.
Przykład
Jak wyjaśniliśmy w rozdziale RESTful Web Services - First Application , umieśćmy http: // localhost: 8080 / UserManagement / rest / UserService / users w POSTMAN z żądaniem GET. Jeśli klikniesz przycisk Podgląd, który znajduje się obok przycisku wysyłania Listonosza, a następnie klikniesz przycisk Wyślij, możesz zobaczyć następujące wyniki.
Tutaj widać, że przeglądarka wysłała żądanie GET i otrzymała treść odpowiedzi w postaci XML.
Adresowanie odnosi się do lokalizowania zasobu lub wielu zasobów znajdujących się na serwerze. Analogiczne jest zlokalizowanie adresu pocztowego osoby.
Każdy zasób w architekturze REST jest identyfikowany za pomocą swojego URI (Uniform Resource Identifier). Identyfikator URI ma następujący format -
<protocol>://<service-name>/<ResourceType>/<ResourceID>
Celem identyfikatora URI jest zlokalizowanie zasobu (ów) na serwerze obsługującym usługę internetową. Innym ważnym atrybutem żądania jest VERB, który identyfikuje operację, która ma zostać wykonana na zasobie. Na przykład w rozdziale RESTful Web Services - pierwsza aplikacja identyfikator URI tohttp://localhost:8080/UserManagement/rest/UserService/users a CZASOWNIK to GET.
Konstruowanie standardowego identyfikatora URI
Poniżej przedstawiono ważne kwestie, które należy wziąć pod uwagę podczas projektowania identyfikatora URI -
Use Plural Noun- Użyj rzeczownika w liczbie mnogiej, aby zdefiniować zasoby. Na przykład użyliśmy użytkowników do zidentyfikowania użytkowników jako zasobu.
Avoid using spaces- Użyj podkreślenia (_) lub łącznika (-), jeśli używasz długiej nazwy zasobu. Na przykład użyj Authorized_users zamiast autoryzowanych% 20users.
Use lowercase letters - Chociaż w identyfikatorze URI nie jest rozróżniana wielkość liter, dobrą praktyką jest przechowywanie adresu URL wyłącznie małymi literami.
Maintain Backward Compatibility- Ponieważ usługa sieciowa jest usługą publiczną, raz upubliczniony identyfikator URI powinien być zawsze dostępny. W przypadku, gdy identyfikator URI zostanie zaktualizowany, przekieruj starszy identyfikator URI do nowego identyfikatora URI przy użyciu kodu stanu HTTP 300.
Use HTTP Verb- Zawsze używaj czasownika HTTP, takiego jak GET, PUT i DELETE, aby wykonywać operacje na zasobie. Używanie nazwy operacji w identyfikatorze URI nie jest dobre.
Przykład
Poniżej znajduje się przykład kiepskiego identyfikatora URI do pobrania użytkownika.
http://localhost:8080/UserManagement/rest/UserService/getUser/1
Poniżej znajduje się przykład dobrego identyfikatora URI do pobrania użytkownika.
http://localhost:8080/UserManagement/rest/UserService/users/1
Jak omówiliśmy we wcześniejszych rozdziałach, usługa sieciowa RESTful używa wielu czasowników HTTP do określenia operacji, która ma zostać wykonana na określonych zasobach. W poniższej tabeli przedstawiono przykłady najczęściej używanych zleceń HTTP.
Sr.No. | Metoda HTTP, URI i operacja |
---|---|
1 | GET http: // localhost: 8080 / UserManagement / rest / UserService / users Pobiera listę użytkowników. (Tylko czytać) |
2 |
GET http: // localhost: 8080 / UserManagement / rest / UserService / users / 1 Pobiera użytkownika o identyfikatorze 1 (Tylko czytać) |
3 |
PUT http: // localhost: 8080 / UserManagement / rest / UserService / users / 2 Wstawia użytkownika o identyfikatorze 2 (Idempotentny) |
4 |
POST http: // localhost: 8080 / UserManagement / rest / UserService / users / 2 Aktualizuje użytkownika o Id 2 (Nie dotyczy) |
5 |
DELETE http: // localhost: 8080 / UserManagement / rest / UserService / users / 1 Usuwa użytkownika o Id 1 (Idempotentny) |
6 |
OPTIONS http: // localhost: 8080 / UserManagement / rest / UserService / users Zawiera listę obsługiwanych operacji w usłudze internetowej. (Tylko czytać) |
7 |
HEAD http: // localhost: 8080 / UserManagement / rest / UserService / users Zwraca tylko nagłówek HTTP, bez treści. (Tylko czytać) |
Należy wziąć pod uwagę następujące punkty.
Operacje GET są tylko do odczytu i są bezpieczne.
Operacje PUT i DELETE są idempotentne, co oznacza, że ich wynik zawsze będzie taki sam, bez względu na to, ile razy te operacje są wywoływane.
Operacje PUT i POST są prawie takie same, z różnicą polegającą tylko na wyniku, w którym operacja PUT jest idempotentna, a operacja POST może spowodować inny wynik.
Przykład
Zaktualizujmy przykład utworzony w rozdziale RESTful Web Services - pierwsza aplikacja, aby utworzyć usługę sieciową, która może wykonywać operacje CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie). Dla uproszczenia użyliśmy pliku I / O, aby zastąpić operacje na bazie danych.
Zaktualizujmy User.java, UserDao.java i UserService.java pliki w pakiecie com.tutorialspoint.
User.java
package com.tutorialspoint;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String profession;
public User(){}
public User(int id, String name, String profession){
this.id = id;
this.name = name;
this.profession = profession;
}
public int getId() {
return id;
}
@XmlElement
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
@XmlElement
public void setName(String name) {
this.name = name;
}
public String getProfession() {
return profession;
}
@XmlElement
public void setProfession(String profession) {
this.profession = profession;
}
@Override
public boolean equals(Object object){
if(object == null){
return false;
}else if(!(object instanceof User)){
return false;
}else {
User user = (User)object;
if(id == user.getId()
&& name.equals(user.getName())
&& profession.equals(user.getProfession())){
return true;
}
}
return false;
}
}
User replace.java
package com.tutorialspoint;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class UserDao {
public List<User> getAllUsers(){
List<User> userList = null;
try {
File file = new File("Users.dat");
if (!file.exists()) {
User user = new User(1, "Mahesh", "Teacher");
userList = new ArrayList<User>();
userList.add(user);
saveUserList(userList);
}
else{
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
userList = (List<User>) ois.readObject();
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return userList;
}
public User getUser(int id){
List<User> users = getAllUsers();
for(User user: users){
if(user.getId() == id){
return user;
}
}
return null;
}
public int addUser(User pUser){
List<User> userList = getAllUsers();
boolean userExists = false;
for(User user: userList){
if(user.getId() == pUser.getId()){
userExists = true;
break;
}
}
if(!userExists){
userList.add(pUser);
saveUserList(userList);
return 1;
}
return 0;
}
public int updateUser(User pUser){
List<User> userList = getAllUsers();
for(User user: userList){
if(user.getId() == pUser.getId()){
int index = userList.indexOf(user);
userList.set(index, pUser);
saveUserList(userList);
return 1;
}
}
return 0;
}
public int deleteUser(int id){
List<User> userList = getAllUsers();
for(User user: userList){
if(user.getId() == id){
int index = userList.indexOf(user);
userList.remove(index);
saveUserList(userList);
return 1;
}
}
return 0;
}
private void saveUserList(List<User> userList){
try {
File file = new File("Users.dat");
FileOutputStream fos;
fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(userList);
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UserService.java
package com.tutorialspoint;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@Path("/UserService")
public class UserService {
UserDao userDao = new UserDao();
private static final String SUCCESS_RESULT = "<result>success</result>";
private static final String FAILURE_RESULT = "<result>failure</result>";
@GET
@Path("/users")
@Produces(MediaType.APPLICATION_XML)
public List<User> getUsers(){
return userDao.getAllUsers();
}
@GET
@Path("/users/{userid}")
@Produces(MediaType.APPLICATION_XML)
public User getUser(@PathParam("userid") int userid){
return userDao.getUser(userid);
}
@PUT
@Path("/users")
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String createUser(@FormParam("id") int id,
@FormParam("name") String name,
@FormParam("profession") String profession,
@Context HttpServletResponse servletResponse) throws IOException{
User user = new User(id, name, profession);
int result = userDao.addUser(user);
if(result == 1){
return SUCCESS_RESULT;
}
return FAILURE_RESULT;
}
@POST
@Path("/users")
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String updateUser(@FormParam("id") int id,
@FormParam("name") String name,
@FormParam("profession") String profession,
@Context HttpServletResponse servletResponse) throws IOException{
User user = new User(id, name, profession);
int result = userDao.updateUser(user);
if(result == 1){
return SUCCESS_RESULT;
}
return FAILURE_RESULT;
}
@DELETE
@Path("/users/{userid}")
@Produces(MediaType.APPLICATION_XML)
public String deleteUser(@PathParam("userid") int userid){
int result = userDao.deleteUser(userid);
if(result == 1){
return SUCCESS_RESULT;
}
return FAILURE_RESULT;
}
@OPTIONS
@Path("/users")
@Produces(MediaType.APPLICATION_XML)
public String getSupportedOperations(){
return "<operations>GET, PUT, POST, DELETE</operations>";
}
}
Teraz korzystając z Eclipse, wyeksportuj swoją aplikację jako plik WAR Filei wdrożyć to samo w Tomcat. Aby utworzyć plik WAR za pomocą eclipse, postępuj zgodnie z tą ścieżką -File → export → Web → War Filei wreszcie wybierz projekt UserManagement i folder docelowy. Aby wdrożyć plik WAR w Tomcat, umieść plik UserManagement.war w plikuTomcat Installation Directory → webapps katalog i start Tomcat.
Testowanie usługi internetowej
Jersey udostępnia interfejsy API do tworzenia klienta usługi sieci Web w celu testowania usług internetowych. Stworzyliśmy przykładową klasę testowąWebServiceTester.java w pakiecie com.tutorialspoint w tym samym projekcie.
WebServiceTester.java
package com.tutorialspoint;
import java.util.List;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
public class WebServiceTester {
private Client client;
private String REST_SERVICE_URL = "
http://localhost:8080/UserManagement/rest/UserService/users";
private static final String SUCCESS_RESULT = "<result>success</result>";
private static final String PASS = "pass";
private static final String FAIL = "fail";
private void init(){
this.client = ClientBuilder.newClient();
}
public static void main(String[] args){
WebServiceTester tester = new WebServiceTester();
//initialize the tester
tester.init();
//test get all users Web Service Method
tester.testGetAllUsers();
//test get user Web Service Method
tester.testGetUser();
//test update user Web Service Method
tester.testUpdateUser();
//test add user Web Service Method
tester.testAddUser();
//test delete user Web Service Method
tester.testDeleteUser();
}
//Test: Get list of all users
//Test: Check if list is not empty
private void testGetAllUsers(){
GenericType<List<User>> list = new GenericType<List<User>>() {};
List<User> users = client
.target(REST_SERVICE_URL)
.request(MediaType.APPLICATION_XML)
.get(list);
String result = PASS;
if(users.isEmpty()){
result = FAIL;
}
System.out.println("Test case name: testGetAllUsers, Result: " + result );
}
//Test: Get User of id 1
//Test: Check if user is same as sample user
private void testGetUser(){
User sampleUser = new User();
sampleUser.setId(1);
User user = client
.target(REST_SERVICE_URL)
.path("/{userid}")
.resolveTemplate("userid", 1)
.request(MediaType.APPLICATION_XML)
.get(User.class);
String result = FAIL;
if(sampleUser != null && sampleUser.getId() == user.getId()){
result = PASS;
}
System.out.println("Test case name: testGetUser, Result: " + result );
}
//Test: Update User of id 1
//Test: Check if result is success XML.
private void testUpdateUser(){
Form form = new Form();
form.param("id", "1");
form.param("name", "suresh");
form.param("profession", "clerk");
String callResult = client
.target(REST_SERVICE_URL)
.request(MediaType.APPLICATION_XML)
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE),
String.class);
String result = PASS;
if(!SUCCESS_RESULT.equals(callResult)){
result = FAIL;
}
System.out.println("Test case name: testUpdateUser, Result: " + result);
}
//Test: Add User of id 2
//Test: Check if result is success XML.
private void testAddUser(){
Form form = new Form();
form.param("id", "2");
form.param("name", "naresh");
form.param("profession", "clerk");
String callResult = client
.target(REST_SERVICE_URL)
.request(MediaType.APPLICATION_XML)
.put(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE),
String.class);
String result = PASS;
if(!SUCCESS_RESULT.equals(callResult)){
result = FAIL;
}
System.out.println("Test case name: testAddUser, Result: " + result );
}
//Test: Delete User of id 2
//Test: Check if result is success XML.
private void testDeleteUser(){
String callResult = client
.target(REST_SERVICE_URL)
.path("/{userid}")
.resolveTemplate("userid", 2)
.request(MediaType.APPLICATION_XML)
.delete(String.class);
String result = PASS;
if(!SUCCESS_RESULT.equals(callResult)){
result = FAIL;
}
System.out.println("Test case name: testDeleteUser, Result: " + result);
}
}
Teraz uruchom tester za pomocą Eclipse. Kliknij plik prawym przyciskiem myszy i postępuj zgodnie z opcjąRun as → Java Application. W konsoli Eclipse zobaczysz następujący wynik -
Test case name: testGetAllUsers, Result: pass
Test case name: testGetUser, Result: pass
Test case name: testUpdateUser, Result: pass
Test case name: testAddUser, Result: pass
Test case name: testDeleteUser, Result: pass
Zgodnie z architekturą REST, usługa sieci Web zgodna z REST nie powinna utrzymywać stanu klienta na serwerze. To ograniczenie nazywa się bezpaństwowością. Klient jest odpowiedzialny za przekazanie kontekstu do serwera, a następnie serwer może przechowywać ten kontekst w celu przetworzenia dalszych żądań klienta. Na przykład sesja utrzymywana przez serwer jest identyfikowana przez identyfikator sesji przekazany przez klienta.
Usługi sieciowe RESTful powinny przestrzegać tego ograniczenia. Widzieliśmy to w rozdziale RESTful Web Services - Methods , że metody usług sieciowych nie przechowują żadnych informacji od klienta, z którego zostały wywołane.
Consider the following URL −
https: // localhost: 8080 / UserManagement / rest / UserService / users / 1
Jeśli trafisz na powyższy adres URL za pomocą przeglądarki, klienta opartego na Javie lub Postmana, wynikiem zawsze będzie XML użytkownika, którego identyfikator wynosi 1, ponieważ serwer nie przechowuje żadnych informacji o kliencie.
<user>
<id>1</id>
<name>mahesh</name>
<profession>1</profession>
</user>
Zalety bezpaństwowości
Oto zalety bezpaństwowości w usługach sieciowych RESTful -
Usługi sieci Web mogą niezależnie traktować każde żądanie metody.
Usługi internetowe nie muszą obsługiwać poprzednich interakcji klienta. Upraszcza projektowanie aplikacji.
Ponieważ sam HTTP jest protokołem bezstanowym, usługi sieciowe RESTful bezproblemowo współpracują z protokołami HTTP.
Wady bezpaństwowości
Oto wady bezpaństwowości w RESTful Web Services -
Usługi internetowe muszą uzyskać dodatkowe informacje w każdym żądaniu, a następnie je zinterpretować, aby uzyskać stan klienta, na wypadek, gdyby trzeba było zająć się interakcjami klienta.
Buforowanie oznacza przechowywanie odpowiedzi serwera w samym kliencie, dzięki czemu klient nie musi wielokrotnie wysyłać żądań serwera dotyczących tego samego zasobu. Odpowiedź serwera powinna zawierać informacje o sposobie buforowania odpowiedzi, aby klient buforował odpowiedź przez określony czas lub nigdy nie buforował odpowiedzi serwera.
Poniżej znajdują się nagłówki, które może mieć odpowiedź serwera w celu skonfigurowania buforowania klienta -
Sr.No. | Nagłówek i opis |
---|---|
1 |
Date Data i godzina utworzenia zasobu. |
2 |
Last Modified Data i godzina zasobu, kiedy był ostatnio modyfikowany. |
3 |
Cache-Control Główny nagłówek do sterowania pamięcią podręczną. |
4 |
Expires Data wygaśnięcia i czas buforowania. |
5 |
Age Czas w sekundach od momentu pobrania zasobu z serwera. |
Nagłówek Cache-Control
Poniżej znajdują się szczegóły nagłówka Cache-Control -
Sr.No. | Dyrektywa i opis |
---|---|
1 |
Public Wskazuje, że zasób może być buforowany przez dowolny składnik. |
2 |
Private Wskazuje, że zasób może być buforowany tylko przez klienta i serwer, żaden pośrednik nie może buforować zasobu. |
3 |
no-cache/no-store Wskazuje, że zasobu nie można buforować. |
4 |
max-age Wskazuje, że buforowanie jest ważne do maksymalnego wieku w sekundach. Następnie klient musi złożyć kolejne żądanie. |
5 |
must-revalidate Wskazanie serwera, aby ponownie zweryfikował zasób, jeśli minął maksymalny wiek. |
Najlepsze praktyki
Zawsze przechowuj statyczne treści, takie jak obrazy, CSS, JavaScript, z datą ważności od 2 do 3 dni.
Nigdy nie utrzymuj zbyt wysokiej daty ważności.
Zawartość dynamiczną należy przechowywać w pamięci podręcznej tylko przez kilka godzin.
Ponieważ usługi sieci Web zgodne ze specyfikacją REST współpracują ze ścieżkami adresów URL HTTP, bardzo ważne jest, aby chronić usługę sieciową zgodną ze specyfikacją REST w taki sam sposób, w jaki zabezpieczana jest witryna internetowa.
Poniżej przedstawiono najlepsze praktyki, których należy przestrzegać podczas projektowania usługi internetowej zgodnej z REST -
Validation- Sprawdź poprawność wszystkich danych wejściowych na serwerze. Chroń swój serwer przed atakami typu SQL lub NoSQL typu injection.
Session Based Authentication - Użyj uwierzytelniania opartego na sesji, aby uwierzytelnić użytkownika za każdym razem, gdy kierowane jest żądanie do metody usługi sieci Web.
No Sensitive Data in the URL - Nigdy nie używaj nazwy użytkownika, hasła ani tokenu sesji w adresie URL, wartości te należy przekazać do usługi sieci Web za pomocą metody POST.
Restriction on Method Execution- Zezwalaj na ograniczone użycie metod, takich jak metody GET, POST i DELETE. Metoda GET nie powinna mieć możliwości usunięcia danych.
Validate Malformed XML/JSON - Sprawdź, czy dane wejściowe są poprawnie sformułowane i przekazywane do metody usługi sieci Web.
Throw generic Error Messages - Metoda usługi sieciowej powinna używać komunikatów o błędach HTTP, takich jak 403, aby pokazać zakaz dostępu itp.
Kod HTTP
Sr.No. | Kod i opis HTTP |
---|---|
1 |
200 OK - pokazuje sukces. |
2 |
201 CREATED- gdy zasób został pomyślnie utworzony za pomocą żądania POST lub PUT. Zwraca łącze do nowo utworzonego zasobu przy użyciu nagłówka lokalizacji. |
3 |
204 NO CONTENT- gdy treść odpowiedzi jest pusta. Na przykład żądanie DELETE. |
4 |
304 NOT MODIFIED- służy do zmniejszania wykorzystania przepustowości sieci w przypadku warunkowych żądań GET. Treść odpowiedzi powinna być pusta. Nagłówki powinny zawierać datę, lokalizację itp. |
5 |
400 BAD REQUEST- stwierdza, że podano nieprawidłowe dane wejściowe. Na przykład błąd weryfikacji, brakujące dane. |
6 |
401 UNAUTHORIZED - stwierdza, że użytkownik używa nieprawidłowego lub nieprawidłowego tokena uwierzytelniającego. |
7 |
403 FORBIDDEN- stwierdza, że użytkownik nie ma dostępu do używanej metody. Na przykład Usuń dostęp bez uprawnień administratora. |
8 |
404 NOT FOUND - stwierdza, że metoda nie jest dostępna. |
9 |
409 CONFLICT- stwierdza sytuację konfliktową podczas wykonywania metody. Na przykład dodanie zduplikowanego wpisu. |
10 |
500 INTERNAL SERVER ERROR - stwierdza, że serwer zgłosił wyjątek podczas wykonywania metody. |
JAX-RSto skrót od JAVA API for RESTful Web Services. JAX-RS to interfejs API języka programowania oparty na JAVA i specyfikacja zapewniająca obsługę utworzonych usług WWW zgodnych z REST. Jego wersja 2.0 została wydana 24 maja 2013 r. JAX-RS wykorzystuje adnotacje dostępne w Java SE 5, aby uprościć tworzenie i wdrażanie usług internetowych opartych na języku JAVA. Zapewnia również obsługę tworzenia klientów dla usług sieci Web zgodnych z REST.
Specyfikacje
Poniżej przedstawiono najczęściej używane adnotacje do mapowania zasobu jako zasobu usługi internetowej.
Sr.No. | Adnotacja i opis |
---|---|
1 |
@Path Względna ścieżka klasy / metody zasobu. |
2 |
@GET Żądanie HTTP Get, używane do pobierania zasobów. |
3 |
@PUT Żądanie HTTP PUT, używane do aktualizacji zasobu. |
4 |
@POST Żądanie HTTP POST, używane do tworzenia nowego zasobu. |
5 |
@DELETE Żądanie HTTP DELETE, używane do usuwania zasobu. |
6 |
@HEAD Żądanie HTTP HEAD, używane do uzyskania stanu dostępności metody. |
7 |
@Produces Wskazuje odpowiedź HTTP wygenerowaną przez usługę sieci Web. Na przykład APPLICATION / XML, TEXT / HTML, APPLICATION / JSON itp. |
8 |
@Consumes Określa typ żądania HTTP. Na przykład application / x-www-formurlencoded do akceptowania danych formularza w treści HTTP podczas żądania POST. |
9 |
@PathParam Wiąże parametr przekazany do metody z wartością w path. |
10 |
@QueryParam Wiąże parametr przekazany do metody z parametrem zapytania w ścieżce. |
11 |
@MatrixParam Wiąże parametr przekazany do metody z parametrem macierzy HTTP w path. |
12 |
@HeaderParam Wiąże parametr przekazany do metody z nagłówkiem HTTP. |
13 |
@CookieParam Wiąże parametr przekazany do metody do pliku Cookie. |
14 |
@FormParam Wiąże parametr przekazany do metody z wartością formularza. |
15 |
@DefaultValue Przypisuje wartość domyślną do parametru przekazanego do metody. |
16 |
@Context Kontekst zasobu. Na przykład HTTPRequest jako kontekst. |
Note- Użyliśmy Jersey, referencyjnej implementacji JAX-RS 2.0 firmy Oracle, w rozdziałach RESTful Web Services - First Application i RESTful Web Services - Methods .