Een gids voor NanoHTTPD

1. Inleiding

NanoHTTPD is een open-source, lichtgewicht webserver geschreven in Java.

In deze zelfstudie maken we een paar REST-API's om de functies ervan te verkennen.

2. Projectconfiguratie

Laten we de NanoHTTPD-kernafhankelijkheid toevoegen aan onze pom.xml:

 org.nanohttpd nanohttpd 2.3.1 

Om een ​​eenvoudige server te maken, moeten we uitbreiden NanoHTTPD en overschrijven zijn dienen methode:

public class App breidt NanoHTTPD uit {public App () gooit IOException {super (8080); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main (String [] args) gooit IOException {new App (); } @Override public Response serve (IHTTPSession-sessie) {return newFixedLengthResponse ("Hallo wereld"); }}

We hebben onze actieve poort gedefinieerd als 8080 en server om als een daemon te werken (geen uitleestime-out).

Zodra we de applicatie starten, wordt de URL // localhost: 8080 / retourneert de Hallo Wereld bericht. We gebruiken NanoHTTPD # newFixedLengthResponse methode als een gemakkelijke manier om een NanoHTTPD.Response voorwerp.

Laten we ons project proberen met cURL:

> curl '// localhost: 8080 /' Hallo wereld

3. REST API

Net als bij HTTP-methoden staat NanoHTTPD GET, POST, PUT, DELETE, HEAD, TRACE en verschillende andere toe.

Simpel gezegd, we kunnen ondersteunde HTTP-werkwoorden vinden via de methode enum. Laten we eens kijken hoe dit uitpakt.

3.1. HTTP GET

Laten we eerst eens kijken naar GET. Stel bijvoorbeeld dat we alleen inhoud willen retourneren wanneer de toepassing een GET-verzoek ontvangt.

In tegenstelling tot Java Servlet-containers hebben we geen doGet beschikbare methode - in plaats daarvan controleren we de waarde via getMethod:

@Override openbare antwoordserver (IHTTPSession-sessie) {if (session.getMethod () == Method.GET) {String itemIdRequestParameter = session.getParameters (). Get ("itemId"). Get (0); retourneer newFixedLengthResponse ("Requested itemId =" + itemIdRequestParameter); } return newFixedLengthResponse (Response.Status.NOT_FOUND, MIME_PLAINTEXT, "De gevraagde bron bestaat niet"); }

Dat was best simpel, toch? Laten we een snelle test uitvoeren door ons nieuwe eindpunt te curlen en te zien dat de verzoekparameter item ID correct wordt gelezen:

> curl '// localhost: 8080 /? itemId = 23Bk8' Gevraagde itemId = 23Bk8

3.2. HTTP-POST

We reageerden eerder op een GET en lazen een parameter uit de URL.

Om de twee meest populaire HTTP-methoden te behandelen, is het tijd dat we een POST afhandelen (en dus de hoofdtekst van het verzoek lezen):

@Override public Response serve (IHTTPSession-sessie) {if (session.getMethod () == Method.POST) {probeer {session.parseBody (new HashMap ()); String requestBody = session.getQueryParameterString (); return newFixedLengthResponse ("Request body =" + requestBody); } catch (IOException | ResponseException e) {// handle}} return newFixedLengthResponse (Response.Status.NOT_FOUND, MIME_PLAINTEXT, "De gevraagde bron bestaat niet"); }
Merk op dat voordat we om de hoofdtekst van het verzoek vroegen, we hebben eerst de parseBody methode. Dat komt omdat we de hoofdtekst van het verzoek wilden laden om later te kunnen ophalen.

We nemen een lichaam op in onze krullen opdracht:

> curl -X POST -d 'deliveryAddress = Washington nr 4 & hoeveelheid = 5' '// localhost: 8080 /' Request body = deliveryAddress = Washington nr 4 & hoeveelheid = 5

De overige HTTP-methoden lijken erg op elkaar, dus die zullen we overslaan.

4. Cross-Origin delen van bronnen

Met CORS maken we communicatie tussen domeinen mogelijk. De meest voorkomende use-case zijn AJAX-oproepen vanuit een ander domein. De eerste benadering die we kunnen gebruiken, is om CORS in te schakelen voor al onze API's. De ... gebruiken -cors argument, geven we toegang tot alle domeinen. We kunnen ook bepalen met welke domeinen we toestaan –Cors = "// dashboard.myApp.com //admin.myapp.com" . De tweede benadering is om CORS in te schakelen voor individuele API's. Laten we eens kijken hoe we het kunnen gebruiken addHeader om dit te behalen:
@Override openbare antwoordservice (IHTTPSession-sessie) {Response response = newFixedLengthResponse ("Hallo wereld"); response.addHeader ("Access-Control-Allow-Origin", "*"); antwoord teruggeven; }

Nu wanneer we krullen, we krijgen onze CORS-header terug:

> curl -v '// localhost: 8080' HTTP / 1.1 200 OK Content-Type: text / html Datum: Do, 13 Jun 2019 03:58:14 GMT Access-Control-Allow-Origin: * Verbinding: keep-alive Lengte inhoud: 11 Hallo wereld

5. Bestand uploaden

NanoHTTPD heeft een aparte afhankelijkheid voor het uploaden van bestanden, dus laten we het aan ons project toevoegen:

 org.nanohttpd nanohttpd-apache-fileupload 2.3.1 javax.servlet javax.servlet-api 4.0.1 voorzien 

Houd er rekening mee dat de servlet-api afhankelijkheid is ook nodig (anders krijgen we een compilatiefout).

Wat NanoHTTPD laat zien, is een klasse met de naam NanoFileUpload:

@Override openbare antwoordservice (IHTTPSession-sessie) {probeer {Lijstbestanden = nieuwe NanoFileUpload (nieuwe DiskFileItemFactory ()). ParseRequest (sessie); int uploadCount = 0; voor (FileItem file: files) {probeer {String fileName = file.getName (); byte [] fileContent = file.get (); Files.write (Paths.get (bestandsnaam), fileContent); geüploadCount ++; } catch (uitzondering uitzondering) {// handle}} return newFixedLengthResponse (Response.Status.OK, MIME_PLAINTEXT, "Geuploade bestanden" + uploadCount + "uit" + files.size ()); } catch (IOException | FileUploadException e) {throw nieuwe IllegalArgumentException ("Kon bestanden van API-verzoek niet verwerken", e); } return newFixedLengthResponse (Response.Status.BAD_REQUEST, MIME_PLAINTEXT, "Fout bij uploaden"); }

Hé, laten we het eens proberen:

> curl -F '[email protected] /pathToFile.txt' '// localhost: 8080' Geüploade bestanden: 1

6. Meerdere routes

EEN nanolet is als een servlet maar heeft een erg laag profiel. We kunnen ze gebruiken om veel routes te definiëren die door een enkele server worden bediend (in tegenstelling tot eerdere voorbeelden met één route).

Laten we eerst de vereiste afhankelijkheid toevoegen voor nanolets:

 org.nanohttpd nanohttpd-nanolets 2.3.1 

En nu breiden we onze hoofdklasse uit met de RouterNanoHTTPD, definieer onze actieve poort en laat de server draaien als een daemon.

De addMappings methode is waar we onze handlers zullen definiëren:

openbare klasse MultipleRoutesExample breidt RouterNanoHTTPD uit {openbare MultipleRoutesExample () gooit IOException {super (8080); addMappings (); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } @Override public void addMappings () {// om de routes in te vullen}}

De volgende stap is om onze te definiëren addMappings methode. Laten we een paar handlers definiëren.

De eerste is een IndexHandler klasse naar "/" pad. Deze klasse wordt geleverd met de NanoHTTPD-bibliotheek en retourneert standaard een Hallo Wereld bericht. We kunnen het getText methode als we een ander antwoord willen:

addRoute ("/", IndexHandler.class); // binnen addMappings-methode

En om onze nieuwe route te testen, kunnen we doen:

> krul '// localhost: 8080' 

Hallo Wereld!

Ten tweede, laten we een nieuw UserHandler klasse die het bestaande uitbreidt DefaultHandler. De route ervoor zal zijn /gebruikers. Hier speelden we wat met de tekst, het MIME-type en de geretourneerde statuscode:

openbare statische klasse UserHandler breidt DefaultHandler {@Override public String getText () {return "UserA, UserB, UserC" uit; } @Override public String getMimeType () {return MIME_PLAINTEXT; } @Override openbare Response.IStatus getStatus () {return Response.Status.OK; }}

Om deze route te noemen, geven we een krullen commando nogmaals:

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

Eindelijk kunnen we het GeneralHandler met een nieuwe StoreHandler klasse. We hebben het geretourneerde bericht aangepast zodat het de storeId sectie van de URL.

openbare statische klasse StoreHandler breidt GeneralHandler uit {@Override public Response get (UriResource uriResource, Map urlParams, IHTTPSession-sessie) {return newFixedLengthResponse ("Ophalen van winkel voor id =" + urlParams.get ("storeId")); }}

Laten we onze nieuwe API eens bekijken:

> curl '// localhost: 8080 / stores / 123' Store ophalen voor id = 123

7. HTTPS

Om HTTPS te kunnen gebruiken, hebben we een certificaat nodig. Raadpleeg ons artikel over SSL voor meer diepgaande informatie.

We kunnen een service als Let's Encrypt gebruiken of we kunnen als volgt eenvoudig een zelfondertekend certificaat genereren:

> keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass wachtwoord -validiteit 360 -keysize 2048 -ext SAN = DNS: localhost, IP: 127.0.0.1 -validiteit 9999

Vervolgens kopiëren we dit keystore.jks naar een locatie op ons klassenpad, zoals bijvoorbeeld de src / main / resources map van een Maven-project.

Daarna kunnen we ernaar verwijzen in een oproep naar NanoHTTPD # makeSSLSocketFactory:

openbare klasse HttpsExample breidt NanoHTTPD uit {openbare HttpsExample () gooit IOException {super (8080); makeSecure (NanoHTTPD.makeSSLSocketFactory ("/keystore.jks", "wachtwoord" .toCharArray ()), null); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } // hoofd- en serveermethoden}

En nu kunnen we het uitproberen. Let op het gebruik van de -onzeker parameter, omdat krullen kan ons zelfondertekende certificaat niet standaard verifiëren:

> curl --insecure '// localhost: 8443' HTTPS-oproep is een succes

8. WebSockets

NanoHTTPD ondersteunt WebSockets.

Laten we de eenvoudigste implementatie van een WebSocket maken. Hiervoor moeten we de extensie NanoWSD klasse. We moeten ook het NanoHTTPD afhankelijkheid voor WebSocket:

 org.nanohttpd nanohttpd-websocket 2.3.1 

Voor onze implementatie zullen we antwoorden met een eenvoudige tekstpayload:

openbare klasse WsdExample breidt NanoWSD uit {openbare WsdExample () gooit IOException {super (8080); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main (String [] args) gooit IOException {new WsdExample (); } @Override beveiligde WebSocket openWebSocket (IHTTPSession ihttpSession) {retourneer nieuwe WsdSocket (ihttpSession); } privé statische klasse WsdSocket breidt WebSocket uit {openbare WsdSocket (IHTTPSession handshakeRequest) {super (handshakeRequest); } // overschrijf de methoden onOpen, onClose, onPong en onException @Override beschermde leegte onMessage (WebSocketFrame webSocketFrame) {probeer {send (webSocketFrame.getTextPayload () + "to you"); } catch (IOException e) {// handle}}}}

In plaats van krullen deze keer gebruiken we wscat:

> wscat -c localhost: 8080 hallo hallo tot ziens tot ziens

9. Conclusie

Samenvattend: we hebben een project gemaakt dat gebruikmaakt van de NanoHTTPD-bibliotheek. Vervolgens hebben we RESTful API's gedefinieerd en meer HTTP-gerelateerde functionaliteiten onderzocht. Uiteindelijk hebben we ook een WebSocket geïmplementeerd.

De implementatie van al deze fragmenten is beschikbaar op GitHub.


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