374

Java SE API だけを使用して、Java で非常に基本的な HTTP サーバー (GET/POST のみをサポート) を作成する方法はありますか? Java SE API は、HTTP クライアント機能をうまくカプセル化していますHttpURLConnectionが、HTTP サーバー機能の類似物はありますか?

明確にするために、ServerSocketオンラインで見た多くの例で私が抱えている問題は、それらが独自のリクエストの解析/応答のフォーマットとエラー処理を行うことです。これは面倒で、エラーが発生しやすく、包括的ではない可能性があります。などの理由で避けたいと思っています。

4

19 に答える 19

531

Java SE 6 以降、Sun Oracle JRE には組み込みの HTTP サーバーがあります。Java 9 モジュール名はjdk.httpserver. com.sun.net.httpserverパッケージの概要には、関連するクラスの概要と例が含まれています。

これは、ドキュメントからコピーペーストされたキックオフの例です。Java 6+でコピーして貼り付けて実行するだけです。
(それにもかかわらず、それを編集しようとしているすべての人にとって、これは見苦しいコードなので、編集しないでください。これはコピーペーストであり、私のものではありません。さらに、元のソースで変更されていない限り、引用を編集しないでください)

package com.stackoverflow.q3732109;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class Test {

    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/test", new MyHandler());
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) throws IOException {
            String response = "This is the response";
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }

}

彼らの例のresponse.length()部分が悪いことに注意してくださいresponse.getBytes().length。その場合でも、getBytes()メソッドは、応答ヘッダーで指定する文字セットを明示的に指定する必要があります。残念ながら、初心者には誤解を招くかもしれませんが、これは基本的なキックオフの例にすぎません。

これを実行して http://localhost:8000/test に移動すると、次の応答が表示されます。

これが応答です


クラスの使用に関してcom.sun.*は、これは一部の開発者の考えに反して、有名な FAQ Why Developers Should Not Write Programs That Call 'sun' Packagesによって絶対に禁止されていないことに注意してください。この FAQ は、パッケージではなく、Oracle JRE による内部使用 (別の JRE でアプリケーションを実行するとアプリケーションが強制終了される) に関するsun.*パッケージ ( など) に関するものです。Sun/Oracle も、Apache などの他のすべての企業と同様に、Java SE API 自体の上にソフトウェアを開発しています。さらに、この固有のものはすべてのJDKに存在する必要があるため、パッケージで発生するような「移植性」の問題はまったくありません。クラスの使用は推奨されません(ただし、推奨されません) 。sun.misc.BASE64Encodercom.sun.*HttpServersun.*com.sun.*禁止) GlassFish (Java EE impl)、Mojarra (JSF impl)、Jersey (JAX-RS impl) などの特定の Java APIの実装に関する場合。

于 2010-09-17T02:34:14.920 に答える
50

NanoHttpdを確認する

NanoHTTPD は、他のアプリケーションに組み込むために設計された軽量の HTTP サーバーであり、Modified BSD ライセンスの下でリリースされています。

これは Github で開発されており、ビルドと単体テストに Apache Maven を使用しています」

于 2010-09-17T02:29:25.977 に答える
25

「Jetty」Web サーバーJettyを見てください。すべての要件を満たすと思われる優れたオープン ソース ソフトウェア。

自分で作成したい場合は、「httpMessage」クラスを見てください。

于 2010-09-17T01:36:15.973 に答える
12

Spark が最も簡単です。クイック スタート ガイドは次のとおりです: http://sparkjava.com/

于 2015-04-02T12:52:06.010 に答える
9

わずか数行のコードで、JDK とサーブレット API のみを使用して、J2EE サーブレットの基本的なサポートを提供する httpserver を作成できます。

これは、他の軽量コンテナーよりもはるかに高速に起動するため、サーブレットの単体テストに非常に役立つことがわかりました (本番環境では jetty を使用しています)。

非常に軽量な http サーバーのほとんどは、サーブレットをサポートしていませんが、サーブレットが必要なので、共有したいと思います。

以下の例は、基本的なサーブレットのサポートを提供するか、まだ実装されていないものに対してスローと UnsupportedOperationException を提供します。基本的な http サポートに com.sun.net.httpserver.HttpServer を使用します。

import java.io.*;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

@SuppressWarnings("deprecation")
public class VerySimpleServletHttpServer {
    HttpServer server;
    private String contextPath;
    private HttpHandler httpHandler;

    public VerySimpleServletHttpServer(String contextPath, HttpServlet servlet) {
        this.contextPath = contextPath;
        httpHandler = new HttpHandlerWithServletSupport(servlet);
    }

    public void start(int port) throws IOException {
        InetSocketAddress inetSocketAddress = new InetSocketAddress(port);
        server = HttpServer.create(inetSocketAddress, 0);
        server.createContext(contextPath, httpHandler);
        server.setExecutor(null);
        server.start();
    }

    public void stop(int secondsDelay) {
        server.stop(secondsDelay);
    }

    public int getServerPort() {
        return server.getAddress().getPort();
    }

}

final class HttpHandlerWithServletSupport implements HttpHandler {

    private HttpServlet servlet;

    private final class RequestWrapper extends HttpServletRequestWrapper {
        private final HttpExchange ex;
        private final Map<String, String[]> postData;
        private final ServletInputStream is;
        private final Map<String, Object> attributes = new HashMap<>();

        private RequestWrapper(HttpServletRequest request, HttpExchange ex, Map<String, String[]> postData, ServletInputStream is) {
            super(request);
            this.ex = ex;
            this.postData = postData;
            this.is = is;
        }

        @Override
        public String getHeader(String name) {
            return ex.getRequestHeaders().getFirst(name);
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            return new Vector<String>(ex.getRequestHeaders().get(name)).elements();
        }

        @Override
        public Enumeration<String> getHeaderNames() {
            return new Vector<String>(ex.getRequestHeaders().keySet()).elements();
        }

        @Override
        public Object getAttribute(String name) {
            return attributes.get(name);
        }

        @Override
        public void setAttribute(String name, Object o) {
            this.attributes.put(name, o);
        }

        @Override
        public Enumeration<String> getAttributeNames() {
            return new Vector<String>(attributes.keySet()).elements();
        }

        @Override
        public String getMethod() {
            return ex.getRequestMethod();
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            return is;
        }

        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }

        @Override
        public String getPathInfo() {
            return ex.getRequestURI().getPath();
        }

        @Override
        public String getParameter(String name) {
            String[] arr = postData.get(name);
            return arr != null ? (arr.length > 1 ? Arrays.toString(arr) : arr[0]) : null;
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            return postData;
        }

        @Override
        public Enumeration<String> getParameterNames() {
            return new Vector<String>(postData.keySet()).elements();
        }
    }

    private final class ResponseWrapper extends HttpServletResponseWrapper {
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        final ServletOutputStream servletOutputStream = new ServletOutputStream() {

            @Override
            public void write(int b) throws IOException {
                outputStream.write(b);
            }
        };

        private final HttpExchange ex;
        private final PrintWriter printWriter;
        private int status = HttpServletResponse.SC_OK;

        private ResponseWrapper(HttpServletResponse response, HttpExchange ex) {
            super(response);
            this.ex = ex;
            printWriter = new PrintWriter(servletOutputStream);
        }

        @Override
        public void setContentType(String type) {
            ex.getResponseHeaders().add("Content-Type", type);
        }

        @Override
        public void setHeader(String name, String value) {
            ex.getResponseHeaders().add(name, value);
        }

        @Override
        public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
            return servletOutputStream;
        }

        @Override
        public void setContentLength(int len) {
            ex.getResponseHeaders().add("Content-Length", len + "");
        }

        @Override
        public void setStatus(int status) {
            this.status = status;
        }

        @Override
        public void sendError(int sc, String msg) throws IOException {
            this.status = sc;
            if (msg != null) {
                printWriter.write(msg);
            }
        }

        @Override
        public void sendError(int sc) throws IOException {
            sendError(sc, null);
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            return printWriter;
        }

        public void complete() throws IOException {
            try {
                printWriter.flush();
                ex.sendResponseHeaders(status, outputStream.size());
                if (outputStream.size() > 0) {
                    ex.getResponseBody().write(outputStream.toByteArray());
                }
                ex.getResponseBody().flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                ex.close();
            }
        }
    }

    public HttpHandlerWithServletSupport(HttpServlet servlet) {
        this.servlet = servlet;
    }

    @SuppressWarnings("deprecation")
    @Override
    public void handle(final HttpExchange ex) throws IOException {
        byte[] inBytes = getBytes(ex.getRequestBody());
        ex.getRequestBody().close();
        final ByteArrayInputStream newInput = new ByteArrayInputStream(inBytes);
        final ServletInputStream is = new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return newInput.read();
            }
        };

        Map<String, String[]> parsePostData = new HashMap<>();

        try {
            parsePostData.putAll(HttpUtils.parseQueryString(ex.getRequestURI().getQuery()));

            // check if any postdata to parse
            parsePostData.putAll(HttpUtils.parsePostData(inBytes.length, is));
        } catch (IllegalArgumentException e) {
            // no postData - just reset inputstream
            newInput.reset();
        }
        final Map<String, String[]> postData = parsePostData;

        RequestWrapper req = new RequestWrapper(createUnimplementAdapter(HttpServletRequest.class), ex, postData, is);

        ResponseWrapper resp = new ResponseWrapper(createUnimplementAdapter(HttpServletResponse.class), ex);

        try {
            servlet.service(req, resp);
            resp.complete();
        } catch (ServletException e) {
            throw new IOException(e);
        }
    }

    private static byte[] getBytes(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        while (true) {
            int r = in.read(buffer);
            if (r == -1)
                break;
            out.write(buffer, 0, r);
        }
        return out.toByteArray();
    }

    @SuppressWarnings("unchecked")
    private static <T> T createUnimplementAdapter(Class<T> httpServletApi) {
        class UnimplementedHandler implements InvocationHandler {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                throw new UnsupportedOperationException("Not implemented: " + method + ", args=" + Arrays.toString(args));
            }
        }

        return (T) Proxy.newProxyInstance(UnimplementedHandler.class.getClassLoader(),
                new Class<?>[] { httpServletApi },
                new UnimplementedHandler());
    }
}
于 2013-11-28T08:55:50.013 に答える
7

また、次のような NIO アプリケーション フレームワークを確認することもできます。

  1. ネッティ: http://jboss.org/netty
  2. Apache Mina: http://mina.apache.org/またはそのサブプロジェクト AsyncWeb: http://mina.apache.org/asyncweb/
于 2010-09-18T12:04:57.060 に答える
6

特にサーブレット機能が必要なく、単にリクエスト/レスポンス オブジェクトにアクセスする必要がある場合は、Simpleを調べることを強くお勧めします。REST が必要な場合は、Jersey をその上に置くことができます。HTML などを出力する必要がある場合は、Freemarker があります。この組み合わせでできることは本当に気に入っています。学ぶべき API は比較的少ないです。

于 2010-09-17T18:29:40.287 に答える
6

このコードは私たちのコードよりも優れています。追加する必要があるのは、javax.servelet.jarorg.mortbay.jetty.jarの 2 つのライブラリだけです。

クラス桟橋:

package jetty;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.mortbay.http.SocketListener;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHttpContext;

public class Jetty {

    public static void main(String[] args) {
        try {
            Server server = new Server();
            SocketListener listener = new SocketListener();      

            System.out.println("Max Thread :" + listener.getMaxThreads() + " Min Thread :" + listener.getMinThreads());

            listener.setHost("localhost");
            listener.setPort(8070);
            listener.setMinThreads(5);
            listener.setMaxThreads(250);
            server.addListener(listener);            

            ServletHttpContext context = (ServletHttpContext) server.getContext("/");
            context.addServlet("/MO", "jetty.HelloWorldServlet");

            server.start();
            server.join();

        /*//We will create our server running at http://localhost:8070
        Server server = new Server();
        server.addListener(":8070");

        //We will deploy our servlet to the server at the path '/'
        //it will be available at http://localhost:8070
        ServletHttpContext context = (ServletHttpContext) server.getContext("/");
        context.addServlet("/MO", "jetty.HelloWorldServlet");

        server.start();
        */

        } catch (Exception ex) {
            Logger.getLogger(Jetty.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
} 

サーブレット クラス:

package jetty;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorldServlet extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
    {
        String appid = httpServletRequest.getParameter("appid");
        String conta = httpServletRequest.getParameter("conta");

        System.out.println("Appid : "+appid);
        System.out.println("Conta : "+conta);

        httpServletResponse.setContentType("text/plain");
        PrintWriter out = httpServletResponse.getWriter();
        out.println("Hello World!");
        out.close();
    }
}
于 2012-09-16T11:36:45.620 に答える
3

チェックアウト簡単。非常に単純な組み込み可能なサーバーであり、さまざまな操作のサポートが組み込まれています。私は特にそのスレッドモデルが大好きです..

すばらしい!

于 2015-07-15T10:43:57.327 に答える
2

Apache Commons HttpCoreプロジェクトはどうですか?

Web サイトから:... HttpCore の目標

  • 最も基本的な HTTP トランスポートの側面の実装
  • 優れたパフォーマンスと API の明快さと表現力のバランス
  • 小さな (予測可能な) メモリ フットプリント
  • 自己完結型ライブラリ (JRE 以外の外部依存関係なし)
于 2013-02-11T21:52:42.017 に答える