1. введение
NanoHttpd -это легкий веб-сервер с открытым исходным кодом, написанный на Java.
В этом руководстве мы создадим несколько API-интерфейсов REST, чтобы изучить его функции.
2. Настройка проекта
Давайте добавим зависимость ядра NanoHttpd в ваш pom.xml :
org.nanohttpd nanohttpd 2.3.1
Чтобы создать простой сервер, нам нужно расширить NanoHttpd и переопределить его обслуживать метод:
public class App extends NanoHTTPD { public App() throws IOException { super(8080); start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main(String[] args ) throws IOException { new App(); } @Override public Response serve(IHTTPSession session) { return newFixedLengthResponse("Hello world"); } }
Мы определили наш рабочий порт как 8080 и сервер для работы в качестве демона (без тайм-аута чтения).
Как только мы запустим приложение, URL-адрес http://localhost:8080/ вернет Привет, мир сообщение. Мы используем NanoHttpd#newFixedLengthResponse метод как удобный способ построения метод как удобный способ построения объект.
Давайте попробуем наш проект с cURL:
> curl 'http://localhost:8080/' Hello world
3. REST API
На пути HTTP-методов NanoHttpd позволяет ПОЛУЧАТЬ, ПУБЛИКОВАТЬ, ПОМЕЩАТЬ, УДАЛЯТЬ, ВОЗГЛАВЛЯТЬ, ОТСЛЕЖИВАТЬ и некоторые другие.
Проще говоря, мы можем найти поддерживаемые HTTP-глаголы с помощью перечисления методов. Давайте посмотрим, как все обернется.
3.1. HTTP GET
Во-первых, давайте взглянем на GET. Скажем, например, что мы хотим возвращать контент только тогда, когда приложение получает запрос GET.
В отличие от контейнеров сервлетов Java , у нас нет метода doGet – вместо этого мы просто проверяем значение с помощью GetMethod :
@Override public Response serve(IHTTPSession session) { if (session.getMethod() == Method.GET) { String itemIdRequestParameter = session.getParameters().get("itemId").get(0); return newFixedLengthResponse("Requested itemId = " + itemIdRequestParameter); } return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "The requested resource does not exist"); }
Это было довольно просто, верно? Давайте проведем быстрый тест, свернув нашу новую конечную точку, и убедимся, что параметр запроса ItemId считан правильно:
> curl 'http://localhost:8080/?itemId=23Bk8' Requested itemId = 23Bk8
3.2. HTTP-СООБЩЕНИЕ
Ранее мы реагировали на GET и считывали параметр из URL-адреса.
Чтобы охватить два самых популярных метода HTTP, нам пора обработать сообщение (и, таким образом, прочитать тело запроса):
@Override public Response serve(IHTTPSession session) { if (session.getMethod() == Method.POST) { try { 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, "The requested resource does not exist"); }
Мы включим тело в нашу команду cURL :
> curl -X POST -d 'deliveryAddress=Washington nr 4&quantity=5''http://localhost:8080/' Request body = deliveryAddress=Washington nr 4&quantity=5
Остальные методы HTTP очень похожи по своей природе, поэтому мы их пропустим.
4. Совместное использование Ресурсов Из Разных Источников
@Override public Response serve(IHTTPSession session) { Response response = newFixedLengthResponse("Hello world"); response.addHeader("Access-Control-Allow-Origin", "*"); return response; }
Теперь, когда мы локон , мы вернем наш заголовок CORS:
> curl -v 'http://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 Content-Length: 11 Hello world
5. Загрузка файлов
NanoHttpd имеет отдельный зависимость для загрузки файлов , так что давайте добавим его в наш проект:
org.nanohttpd nanohttpd-apache-fileupload 2.3.1 javax.servlet javax.servlet-api 4.0.1 provided
Обратите внимание, что servlet-api dependency также необходим (в противном случае мы получим ошибку компиляции).
То, что предоставляет NanoHttpd, – это класс, называемый Nano File Upload :
@Override public Response serve(IHTTPSession session) { try { Listfiles = new NanoFileUpload(new DiskFileItemFactory()).parseRequest(session); int uploadedCount = 0; for (FileItem file : files) { try { String fileName = file.getName(); byte[] fileContent = file.get(); Files.write(Paths.get(fileName), fileContent); uploadedCount++; } catch (Exception exception) { // handle } } return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT, "Uploaded files " + uploadedCount + " out of " + 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, "Error when uploading"); }
Эй, давай попробуем:
> curl -F '[email protected]/pathToFile.txt' 'http://localhost:8080' Uploaded files: 1
6. Несколько Маршрутов
Во-первых, давайте добавим необходимую зависимость для nano lets :
org.nanohttpd nanohttpd-nanolets 2.3.1
А теперь мы расширим наш основной класс, используя RouterNanoHTTPD, определим наш рабочий порт и запустим сервер как демон.
Метод addmapping – это место, где мы определим наши обработчики:
public class MultipleRoutesExample extends RouterNanoHTTPD { public MultipleRoutesExample() throws IOException { super(8080); addMappings(); start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } @Override public void addMappings() { // todo fill in the routes } }
Следующий шаг – определить наши addmapping метод. Давайте определим несколько обработчиков.
Первый-это Обработчик индекса класса к пути”/”. Этот класс поставляется с библиотекой NanoHttpd и по умолчанию возвращает сообщение Hello World . Мы можем переопределить метод getText , когда нам нужен другой ответ:
addRoute("/", IndexHandler.class); // inside addMappings method
И чтобы проверить наш новый маршрут, мы можем сделать:
> curl 'http://localhost:8080'Hello world!
Во-вторых, давайте создадим новый Обработчик пользователя класс, который расширяет существующий Обработчик по умолчанию. Маршрут для него будет/ пользователи . Здесь мы поиграли с текстом, типом MIME и возвращенным кодом состояния:
public static class UserHandler extends 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; } }
Чтобы вызвать этот маршрут, мы снова выполним команду cURL :
> curl -X POST 'http://localhost:8080/users' UserA, UserB, UserC
Наконец, мы можем исследовать Общий обработчик с новым Обработчик хранилища класс. Мы изменили возвращенное сообщение, включив в него StoreID раздел URL-адреса.
public static class StoreHandler extends GeneralHandler { @Override public Response get( UriResource uriResource, MapurlParams, IHTTPSession session) { return newFixedLengthResponse("Retrieving store for id = " + urlParams.get("storeId")); } }
Давайте проверим наш новый API:
> curl 'http://localhost:8080/stores/123' Retrieving store for id = 123
7. HTTPS
Чтобы использовать HTTPS, нам понадобится сертификат. Пожалуйста, обратитесь к нашей статье о SSL для получения более подробной информации.
Мы могли бы использовать такую службу, как Let’s Encrypt , или мы можем просто сгенерировать самозаверяющий сертификат следующим образом:
> keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048 -ext SAN=DNS:localhost,IP:127.0.0.1 -validity 9999
Затем мы скопируем это keystore.jks в папку на нашем пути к классу, например, в папку src/main/resources проекта Maven.
После этого мы можем сослаться на него в вызове NanoHttpd#make SSLSocketFactory :
public class HttpsExample extends NanoHTTPD { public HttpsExample() throws IOException { super(8080); makeSecure(NanoHTTPD.makeSSLSocketFactory( "/keystore.jks", "password".toCharArray()), null); start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } // main and serve methods }
А теперь мы можем попробовать. Пожалуйста, обратите внимание на использование параметра — небезопасно , потому что cURL не сможет проверить наш самозаверяющий сертификат по умолчанию:
> curl --insecure 'https://localhost:8443' HTTPS call is a success
8. WebSockets
NanoHttpd поддерживает WebSockets .
Давайте создадим простейшую реализацию WebSocket. Для этого нам нужно расширить класс Nanobsd . Нам также нужно будет добавить зависимость NanoHttpd для WebSocket:
org.nanohttpd nanohttpd-websocket 2.3.1
Для нашей реализации мы просто ответим простым текстом.:
public class WsdExample extends NanoWSD { public WsdExample() throws IOException { super(8080); start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } public static void main(String[] args) throws IOException { new WsdExample(); } @Override protected WebSocket openWebSocket(IHTTPSession ihttpSession) { return new WsdSocket(ihttpSession); } private static class WsdSocket extends WebSocket { public WsdSocket(IHTTPSession handshakeRequest) { super(handshakeRequest); } //override onOpen, onClose, onPong and onException methods @Override protected void onMessage(WebSocketFrame webSocketFrame) { try { send(webSocketFrame.getTextPayload() + " to you"); } catch (IOException e) { // handle } } } }
Вместо cURL на этот раз мы будем использовать wscat :
> wscat -c localhost:8080 hello hello to you bye bye to you
9. Заключение
Подводя итог, мы создали проект, который использует библиотеку NanoHttpd. Затем мы определили RESTful API и изучили больше функций, связанных с HTTP. В конце концов, мы также внедрили WebSocket.
Реализация всех этих фрагментов доступна на GitHub .