En guide til NanoHTTPD

1. Introduksjon

NanoHTTPD er en åpen kildekode, lett, webserver skrevet i Java.

I denne opplæringen vil vi lage noen REST APIer for å utforske funksjonene.

2. Prosjektoppsett

La oss legge til NanoHTTPD-kjerneavhengigheten til vår pom.xml:

 org.nanohttpd nanohttpd 2.3.1 

For å lage en enkel server, må vi utvide NanoHTTPD og overstyre dens tjene metode:

offentlig klasse App utvider NanoHTTPD {offentlig App () kaster IOException {super (8080); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } offentlig statisk ugyldig hoved (String [] args) kaster IOException {ny app (); } @ Override public Response serve (IHTTPSession session) {return newFixedLengthResponse ("Hello world"); }}

Vi definerte vår kjørende port som 8080 og server for å fungere som en demon (ingen tidsavbrudd for lesing).

Når vi starter applikasjonen, URL // lokal vert: 8080 / vil returnere Hei Verden beskjed. Vi bruker NanoHTTPD # newFixedLengthResponse metode som en praktisk måte å bygge en NanoHTTPD.Response gjenstand.

La oss prøve prosjektet vårt med cURL:

> krøll '// localhost: 8080 /' Hei verden

3. REST API

I veien for HTTP-metoder tillater NanoHTTPD GET, POST, PUT, SLETT, HEAD, TRACE og flere andre.

Enkelt sagt, vi kan finne støttede HTTP-verb via metoden enum. La oss se hvordan dette spiller ut.

3.1. HTTP FÅ

La oss først se på GET. Si for eksempel at vi bare vil returnere innhold når applikasjonen mottar en GET-forespørsel.

I motsetning til Java Servlet-containere har vi ikke en doGet metode tilgjengelig - i stedet sjekker vi bare verdien via getMethod:

@Override public Response serve (IHTTPSession session) {if (session.getMethod () == Method.GET) {String itemIdRequestParameter = session.getParameters (). Get ("itemId"). Get (0); returner newFixedLengthResponse ("Requested itemId =" + itemIdRequestParameter); } returner newFixedLengthResponse (Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Den forespurte ressursen eksisterer ikke"); }

Det var ganske enkelt, ikke sant? La oss kjøre en rask test ved å krølle vårt nye endepunkt og se at forespørselsparameteren itemId leses riktig:

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

3.2. HTTP POST

Vi har tidligere reagert på en GET og lest en parameter fra URL-en.

For å dekke de to mest populære HTTP-metodene, er det på tide for oss å håndtere en POST (og dermed lese forespørselen):

@Override public Response serve (IHTTPSession session) {if (session.getMethod () == Method.POST) {try {session.parseBody (new HashMap ()); StrengforespørselBody = session.getQueryParameterString (); returner newFixedLengthResponse ("Request body =" + requestBody); } catch (IOException | ResponseException e) {// handle}} returner newFixedLengthResponse (Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Den forespurte ressursen eksisterer ikke"); }
Legg merke til at før vi ba om anmodningsorganet, vi kalte først parseBody metode. Det er fordi vi ønsket å laste forespørselen for senere henting.

Vi vil inkludere en kropp i vår krøll kommando:

> krøll -X POST -d 'deliveryAddress = Washington nr 4 & antall = 5' '// localhost: 8080 /' Forespørsel kropp = levering Adresse = Washington nr 4 & kvantitet = 5

De resterende HTTP-metodene er veldig like i naturen, så vi hopper over dem.

4. Ressursdeling på tvers av opprinnelse

Ved hjelp av CORS muliggjør vi kommunikasjon på tvers av domener. Den vanligste brukssaken er AJAX-samtaler fra et annet domene. Den første tilnærmingen vi kan bruke er å aktivere CORS for alle API-ene våre. Bruker -kurver argument, vil vi gi tilgang til alle domener. Vi kan også definere hvilke domener vi tillater med –Cors = ”// dashboard.myApp.com //admin.myapp.com” . Den andre tilnærmingen er å aktivere CORS for individuelle APIer. La oss se hvordan du bruker addHeader for å oppnå dette:
@ Override public Response serve (IHTTPSession session) {Response response = newFixedLengthResponse ("Hello world"); response.addHeader ("Access-Control-Allow-Origin", "*"); returrespons; }

Nå når vi krøll, får vi CORS-overskriften vår tilbake:

> curl -v '// localhost: 8080' HTTP / 1.1 200 OK Content-Type: text / html Date: Thu, 13 Jun 2019 03:58:14 GMT Access-Control-Allow-Origin: * Connection: keep-alive Innholdslengde: 11 Hei verden

5. Filopplasting

NanoHTTPD har en egen avhengighet for filopplasting, så la oss legge den til i prosjektet vårt:

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

Vær oppmerksom på at servlet-api avhengighet er også nødvendig (ellers får vi en kompileringsfeil).

Det NanoHTTPD avslører er en klasse som heter NanoFileLast opp:

@ Override public Response serve (IHTTPSession session) {prøv {List files = new NanoFileUpload (new DiskFileItemFactory ()). ParseRequest (session); int uploadedCount = 0; for (FileItem-fil: filer) {prøv {String fileName = file.getName (); byte [] fileContent = file.get (); Files.write (Paths.get (fileName), fileContent); uploadedCount ++; } fange (Unntak unntak) {// handle}} returnere newFixedLengthResponse (Response.Status.OK, MIME_PLAINTEXT, "Uploaded files" + uploadedCount + "out of" + files.size ()); } catch (IOException | FileUploadException e) {throw new IllegalArgumentException ("Kunne ikke håndtere filer fra API-forespørsel", e); } returner newFixedLengthResponse (Response.Status.BAD_REQUEST, MIME_PLAINTEXT, "Feil ved opplasting"); }

Hei, la oss prøve det:

> curl -F '[email protected] /pathToFile.txt' '// localhost: 8080' Lastede filer: 1

6. Flere ruter

EN nanolet er som en servlet, men har en veldig lav profil. Vi kan bruke dem til å definere mange ruter som serveres av en enkelt server (i motsetning til tidligere eksempler med en rute).

For det første, la oss legge til den nødvendige avhengigheten for nanoletter:

 org.nanohttpd nanohttpd-nanolets 2.3.1 

Og nå utvider vi hovedklassen vår med RuterNanoHTTPD, definere vår kjørende port og få serveren til å kjøre som en demon.

De addMappings metoden er der vi skal definere håndtererne våre:

offentlig klasse MultipleRoutesExample utvider RouterNanoHTTPD {public MultipleRoutesExample () kaster IOException {super (8080); addMappings (); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } @ Override public void addMappings () {// todo fyll ut rutene}}

Det neste trinnet er å definere vår addMappings metode. La oss definere noen få håndtere.

Den første er en IndexHandler klasse til “/” sti. Denne klassen kommer med NanoHTTPD-biblioteket og returnerer som standard a Hei Verden beskjed. Vi kan overstyre getText metode når vi ønsker et annet svar:

addRoute ("/", IndexHandler.class); // inne i addMappings-metoden

Og for å teste vår nye rute kan vi gjøre:

> krøll '// localhost: 8080' 

Hei Verden!

For det andre, la oss lage et nytt UserHandler klasse som utvider den eksisterende DefaultHandler. Ruten for det vil være /brukere. Her lekte vi rundt med teksten, MIME-typen og statuskoden som ble returnert:

offentlig statisk klasse UserHandler utvider DefaultHandler {@Override public String getText () {return "UserA, UserB, UserC"; } @ Override public String getMimeType () {return MIME_PLAINTEXT; } @ Override offentlig Response.IStatus getStatus () {return Response.Status.OK; }}

For å ringe denne ruten, utsteder vi en krøll kommandoen igjen:

> krøll -X POST '// localhost: 8080 / brukere' UserA, UserB, UserC

Endelig kan vi utforske GeneralHandler med en ny StoreHandler klasse. Vi endret den returnerte meldingen for å inkludere storeId delen av URL-en.

offentlig statisk klasse StoreHandler utvider GeneralHandler {@Override public Response get (UriResource uriResource, Map urlParams, IHTTPSession session) {return newFixedLengthResponse ("Henter butikk for id =" + urlParams.get ("storeId")); }}

La oss sjekke vår nye API:

> curl '// localhost: 8080 / stores / 123' Henter butikk for id = 123

7. HTTPS

For å kunne bruke HTTPS trenger vi et sertifikat. Se artikkelen vår om SSL for mer grundig informasjon.

Vi kan bruke en tjeneste som Let's Encrypt, eller vi kan ganske enkelt generere et selvsignert sertifikat som følger:

> keytool -genkey -keyalg RSA -alias selvstendig -keystore keystore.jks -storepass passord -validity 360 -keysize 2048 -ext SAN = DNS: localhost, IP: 127.0.0.1 -validitet 9999

Deretter kopierer vi dette keystore.jks til et sted på klassestien vår, som sier src / main / resources mappe til et Maven-prosjekt.

Etter det kan vi referere til det i en samtale til NanoHTTPD # makeSSLSocketFactory:

offentlig klasse HttpsExample utvider NanoHTTPD {public HttpsExample () kaster IOException {super (8080); makeSecure (NanoHTTPD.makeSSLSocketFactory ("/keystore.jks", "passord" .toCharArray ()), null); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } // hoved- og serveringsmetoder}

Og nå kan vi prøve det. Vær oppmerksom på bruken av -utrygg parameter, fordi krøll vil ikke kunne bekrefte vårt selvsignerte sertifikat som standard:

> curl --insecure '// localhost: 8443' HTTPS-samtale er en suksess

8. WebSockets

NanoHTTPD støtter WebSockets.

La oss lage den enkleste implementeringen av en WebSocket. For dette må vi utvide NanoWSD klasse. Vi må også legge til NanoHTTPD avhengighet for WebSocket:

 org.nanohttpd nanohttpd-websocket 2.3.1 

For implementeringen vil vi bare svare med en enkel tekstnyttelast:

offentlig klasse WsdExample utvider NanoWSD {public WsdExample () kaster IOException {super (8080); start (NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main (String [] args) kaster IOException {new WsdExample (); } @ Override-beskyttet WebSocket openWebSocket (IHTTPSession ihttpSession) {returner ny WsdSocket (ihttpSession); } privat statisk klasse WsdSocket utvider WebSocket {public WsdSocket (IHTTPSession handshakeRequest) {super (handshakeRequest); } // overstyring onOpen, onClose, onPong og onException metoder @Override beskyttet ugyldig onMessage (WebSocketFrame webSocketFrame) {prøv {send (webSocketFrame.getTextPayload () + "til deg"); } fangst (IOException e) {// håndtak}}}}

I stedet for krøll denne gangen bruker vi wscat:

> wscat -c localhost: 8080 hei hei til deg farvel til deg

9. Konklusjon

For å oppsummere har vi laget et prosjekt som bruker NanoHTTPD-biblioteket. Deretter definerte vi RESTful APIer og utforsket flere HTTP-relaterte funksjoner. Til slutt implementerte vi også en WebSocket.

Implementeringen av alle disse kodebitene er tilgjengelig på GitHub.


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