Sprievodca po NanoHTTPD

1. Úvod

NanoHTTPD je ľahký webový server otvoreného zdroja napísaný v prostredí Java.

V tomto tutoriáli vytvoríme niekoľko rozhraní REST API, aby sme preskúmali jeho funkcie.

2. Nastavenie projektu

Pridajme k nášmu závislosť jadra NanoHTTPD pom.xml:

 org.nanohttpd nanohttpd 2.3.1 

Aby sme vytvorili jednoduchý server, musíme ho rozšíriť NanoHTTPD a prepísať jeho slúžiť metóda:

public class App extends NanoHTTPD {public App () throws IOException {super (8080); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main (String [] args) hodí IOException {new App (); } @Override public Response serve (relácia IHTTPSession) {return newFixedLengthResponse ("Hello world"); }}

Náš bežiaci port sme definovali ako 8080 a server bude pracovať ako démon (žiadny časový limit čítania).

Akonáhle spustíme aplikáciu, URL // localhost: 8080 / vráti Ahoj svet správa. Používame NanoHTTPD # newFixedLengthResponse metóda ako pohodlný spôsob stavby a NanoHTTPD. Odozva objekt.

Vyskúšajme náš projekt s cURL:

> curl '// localhost: 8080 /' Hello world

3. REST API

V spôsobe metód HTTP umožňuje NanoHTTPD GET, POST, PUT, DELETE, HEAD, TRACE a niekoľko ďalších.

Jednoducho povedané, podporované slovesá HTTP nájdeme prostredníctvom metódy enum. Pozrime sa, ako sa to bude hrať.

3.1. ZÍSKAŤ HTTP

Najprv sa pozrime na GET. Povedzme napríklad, že chceme vrátiť obsah, iba keď aplikácia prijme žiadosť GET.

Na rozdiel od kontajnerov Java Servlet nemáme doGet dostupná metóda - namiesto toho iba skontrolujeme hodnotu pomocou getMethod:

@Override public Response serve (relácia IHTTPSession) {if (session.getMethod () == Method.GET) {String itemIdRequestParameter = session.getParameters (). Get ("itemId"). Get (0); return newFixedLengthResponse ("Požadovaný itemId =" + itemIdRequestParameter); } return newFixedLengthResponse (Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Požadovaný zdroj neexistuje"); }

To bolo dosť jednoduché, však? Poďme spustiť rýchly test natočením nášho nového koncového bodu a uvidíme, že parameter požiadavky itemId sa číta správne:

> curl '// localhost: 8080 /? itemId = 23Bk8' požadovaný itemId = 23Bk8

3.2. HTTP POST

Predtým sme reagovali na GET a načítali sme parameter z adresy URL.

Aby sme pokryli dve najpopulárnejšie metódy HTTP, je načase, aby sme spracovali POST (a teda prečítali telo žiadosti):

@Override public Response serve (relácia IHTTPSession) {if (session.getMethod () == Method.POST) {try {session.parseBody (new HashMap ()); Reťazec requestBody = session.getQueryParameterString (); return newFixedLengthResponse ("Body žiadosti =" + requestBody); } catch (IOException | ResponseException e) {// handle}} return newFixedLengthResponse (Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Požadovaný zdroj neexistuje"); }
Všimnite si, že predtým, ako sme požiadali o orgán žiadosti, najskôr sme volali parseBody metóda. Je to preto, že sme chceli načítať telo žiadosti pre neskoršie načítanie.

Do našej vložíme telo curl príkaz:

> curl -X POST -d 'deliveryAddress = Washington č. 4 & množstvo = 5' '// localhost: 8080 /' požiadavka body = deliveryAddress = Washington č. 4 & množstvo = 5

Zvyšné metódy HTTP sú svojou povahou veľmi podobné, takže ich preskočíme.

4. Zdieľanie zdrojov medzi pôvodu

Pomocou CORS umožňujeme komunikáciu medzi doménami. Najbežnejším prípadom použitia sú hovory AJAX z inej domény. Prvý prístup, ktorý môžeme použiť, je povolenie CORS pre všetky naše API. Pomocou -kordy argument umožníme prístup do všetkých domén. Môžeme tiež definovať, s ktorými doménami povolíme –Cors = ”// dashboard.myApp.com //admin.myapp.com” . Druhým prístupom je povolenie CORS pre jednotlivé API. Pozrime sa, ako sa používa addHeader dosiahnuť toto:
@Override public Response serve (relácia IHTTPSession) {Response response = newFixedLengthResponse ("Hello world"); response.addHeader ("Access-Control-Allow-Origin", "*"); spätná odozva; }

Teraz, keď sme curl, dostaneme späť našu hlavičku CORS:

> curl -v '// localhost: 8080' HTTP / 1.1 200 OK Typ obsahu: text / html Dátum: št, 13. júna 2019 03:58:14 GMT Access-Control-Allow-Origin: * Pripojenie: keep-alive Dĺžka obsahu: 11 Dobrý deň, svet

5. Odovzdanie súboru

NanoHTTPD má samostatnú závislosť od nahrávania súborov, pridajme ho teda k nášmu projektu:

 org.nanohttpd nanohttpd-apache-fileupload 2.3.1 poskytnutý javax.servlet javax.servlet-api 4.0.1 

Upozorňujeme, že servlet-api je tiež potrebná závislosť (inak dostaneme chybu kompilácie).

To, čo NanoHTTPD odhaľuje, je trieda s názvom NanoFileUpload:

@Override public Response serve (relácia IHTTPSession) {try {List files = new NanoFileUpload (new DiskFileItemFactory ()). ParseRequest (session); int uploadedCount = 0; pre (FileItem file: files) {try {String fileName = file.getName (); byte [] fileContent = file.get (); Files.write (Paths.get (názov súboru), fileContent); uploadedCount ++; } catch (Výnimka výnimky) {// handle}} return newFixedLengthResponse (Response.Status.OK, MIME_PLAINTEXT, "Nahrané súbory" + uploadedCount + "z" + files.size ()); } catch (IOException | FileUploadException e) {throw new IllegalArgumentException ("Could not handle files from API request", e); } return newFixedLengthResponse (Response.Status.BAD_REQUEST, MIME_PLAINTEXT, "Chyba pri nahrávaní"); }

Hej, skúsme to:

> curl -F '[chránený e-mailom] /pathToFile.txt' '// localhost: 8080' Nahrané súbory: 1

6. Viaceré trasy

A nanolet je ako servlet, ale má veľmi nízky profil. Môžeme ich použiť na definovanie mnohých ciest obsluhovaných jedným serverom (na rozdiel od predchádzajúcich príkladov s jednou cestou).

Najskôr pridajme požadovanú závislosť pre nanolety:

 org.nanohttpd nanohttpd-nanolety 2.3.1 

A teraz rozšírime našu hlavnú triedu pomocou RouterNanoHTTPD, definujte náš bežiaci port a nechajte server bežať ako démon.

The addMappings metóda je miesto, kde definujeme naše obslužné rutiny:

public class MultipleRoutesExample extends RouterNanoHTTPD {public MultipleRoutesExample () throws IOException {super (8080); addMappings (); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } @Override public void addMappings () {// vyplňte trasy}}

Ďalším krokom je definovanie našej addMappings metóda. Definujme niekoľko obslužných rutín.

Prvý z nich je IndexHandler triedy na cestu „/“. Táto trieda sa dodáva s knižnicou NanoHTTPD a predvolene sa vracia a Ahoj svet správa. Môžeme prepísať getText metóda, keď chceme inú odpoveď:

addRoute ("/", IndexHandler.class); // vo vnútri metódy addMappings

A aby sme vyskúšali našu novú trasu, môžeme urobiť:

> curl '// localhost: 8080' 

Ahoj svet!

Po druhé, vytvorme nový UserHandler triedy, ktorá rozširuje existujúce DefaultHandler. Trasa pre ňu bude /používateľov. Tu sme sa pohrali s textom, typom MIME a vráteným stavovým kódom:

verejná statická trieda UserHandler rozširuje DefaultHandler {@Override public String getText () {return "UserA, UserB, UserC"; } @Override public String getMimeType () {return MIME_PLAINTEXT; } @Override public Response.IStatus getStatus () {return Response.Status.OK; }}

Na volanie tejto trasy vydáme a curl príkaz znova:

> curl -X POST '// localhost: 8080 / users' UserA, UserB, UserC

Nakoniec môžeme preskúmať GeneralHandler s novým StoreHandler trieda. Vrátenú správu sme upravili tak, aby obsahovala storeId časť adresy URL.

public static class StoreHandler extends GeneralHandler {@Override public Response get (UriResource uriResource, Map urlParams, IHTTPSession session) {return newFixedLengthResponse ("Retrieving store for id =" + urlParams.get ("storeId")); }}

Poďme skontrolovať naše nové API:

> curl '// localhost: 8080 / stores / 123' Načítanie obchodu pre id = 123

7. HTTPS

Aby sme mohli používať HTTPS, budeme potrebovať certifikát. Podrobnejšie informácie nájdete v našom článku o protokole SSL.

Mohli by sme použiť službu ako Let's Encrypt alebo môžeme jednoducho vygenerovať certifikát s vlastným podpisom takto:

> keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass heslo -validita 360-veľkosť kľúča 2048 -ďalšia SAN = DNS: localhost, IP: 127.0.0.1 -validita 9999

Ďalej by sme to skopírovali keystore.jks na miesto na našej triede, napríklad src / main / resources priečinok projektu Maven.

Potom to môžeme vo výzve odkázať NanoHTTPD # makeSSLSocketFactory:

public class HttpsExample extends NanoHTTPD {public HttpsExample () throws IOException {super (8080); makeSecure (NanoHTTPD.makeSSLSocketFactory ("/keystore.jks", "heslo" .toCharArray ()), null); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } // hlavné a obslužné metódy}

A teraz to môžeme vyskúšať. Všimnite si prosím použitie -neistý parameter, pretože curl predvolene nebude môcť overiť náš certifikát s vlastným podpisom:

> curl --insecure '// localhost: 8443' Volanie HTTPS je úspešné

8. Webové zásuvky

NanoHTTPD podporuje WebSockets.

Vytvorme najjednoduchšiu implementáciu WebSocketu. Z tohto dôvodu budeme musieť rozšíriť NanoWSD trieda. Budeme tiež musieť pridať NanoHTTPD závislosť pre WebSocket:

 org.nanohttpd nanohttpd-websocket 2.3.1 

Pre našu implementáciu odpovieme iba jednoduchým textovým užitočným zaťažením:

public class WsdExample extends NanoWSD {public WsdExample () throws IOException {super (8080); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main (String [] args) vyvolá IOException {new WsdExample (); } @Override chránený WebSocket openWebSocket (IHTTPSession ihttpSession) {vrátiť nový WsdSocket (ihttpSession); } súkromná statická trieda WsdSocket rozširuje WebSocket {public WsdSocket (IHTTPSession handshakeRequest) {super (handshakeRequest); } // prepísať metódy onOpen, onClose, onPong a onException @Override chránené void onMessage (WebSocketFrame webSocketFrame) {try {send (webSocketFrame.getTextPayload () + "to you"); } chytit (IOException e) {// handle}}}}

Namiesto curl tentokrát použijeme wscat:

> wscat -c localhost: 8080 ahoj ahoj ahoj ahoj ahoj

9. Záver

Keď to zhrnieme, vytvorili sme projekt, ktorý využíva knižnicu NanoHTTPD. Ďalej sme definovali rozhrania RESTful API a preskúmali ďalšie funkcie súvisiace s protokolom HTTP. Nakoniec sme implementovali aj WebSocket.

Implementácia všetkých týchto úryvkov je k dispozícii na GitHub.


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