【问题标题】:Java: Simple HTTP Server application that responds in JSONJava:以 JSON 响应的简单 HTTP Server 应用程序
【发布时间】:2015-04-18 17:09:06
【问题描述】:

我想用 Java 创建一个非常简单的 HTTP 服务器应用程序。

例如,如果我在端口 8080 中的 localhost 上运行服务器,并从浏览器进行以下调用,我想获得一个 Json 数组,其中包含字符串“你好世界!”:

http://localhost:8080/func1?param1=123&param2=456

我想在服务器中有这样的东西(非常抽象的代码):

// Retunrs JSON String
String func1(String param1, String param2) {
    // Do Something with the params
    String jsonFormattedResponse = "['hello world!']";

    return jsonFormattedResponse;
}

我猜这个函数实际上不应该“返回”json,而是使用一些 HTTP 响应处理程序或类似的东西来发送它......

无需熟悉多种具有特殊功能和方法的第三方库,最简单的方法是什么?

【问题讨论】:

  • 尝试查找有关 servlet 的教程。示例:tutorialspoint.com/servlets
  • Spring Boot 使这类事情变得非常简单,但正如您所暗示的那样,其底层具有 巨大 数量的复杂性。不过,它就像几个 POM 依赖项和大约 5 行代码一样简单。 Spring Boot 网站上有很好的 HOWTO。

标签: java java-server


【解决方案1】:

您可以使用 com.sun.net.httpserver 包中的类:

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JsonServer {
    private static final String HOSTNAME = "localhost";
    private static final int PORT = 8080;
    private static final int BACKLOG = 1;

    private static final String HEADER_ALLOW = "Allow";
    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    private static final Charset CHARSET = StandardCharsets.UTF_8;

    private static final int STATUS_OK = 200;
    private static final int STATUS_METHOD_NOT_ALLOWED = 405;

    private static final int NO_RESPONSE_LENGTH = -1;

    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String ALLOWED_METHODS = METHOD_GET + "," + METHOD_OPTIONS;

    public static void main(final String... args) throws IOException {
        final HttpServer server = HttpServer.create(new InetSocketAddress(HOSTNAME, PORT), BACKLOG);
        server.createContext("/func1", he -> {
            try {
                final Headers headers = he.getResponseHeaders();
                final String requestMethod = he.getRequestMethod().toUpperCase();
                switch (requestMethod) {
                    case METHOD_GET:
                        final Map<String, List<String>> requestParameters = getRequestParameters(he.getRequestURI());
                        // do something with the request parameters
                        final String responseBody = "['hello world!']";
                        headers.set(HEADER_CONTENT_TYPE, String.format("application/json; charset=%s", CHARSET));
                        final byte[] rawResponseBody = responseBody.getBytes(CHARSET);
                        he.sendResponseHeaders(STATUS_OK, rawResponseBody.length);
                        he.getResponseBody().write(rawResponseBody);
                        break;
                    case METHOD_OPTIONS:
                        headers.set(HEADER_ALLOW, ALLOWED_METHODS);
                        he.sendResponseHeaders(STATUS_OK, NO_RESPONSE_LENGTH);
                        break;
                    default:
                        headers.set(HEADER_ALLOW, ALLOWED_METHODS);
                        he.sendResponseHeaders(STATUS_METHOD_NOT_ALLOWED, NO_RESPONSE_LENGTH);
                        break;
                }
            } finally {
                he.close();
            }
        });
        server.start();
    }

    private static Map<String, List<String>> getRequestParameters(final URI requestUri) {
        final Map<String, List<String>> requestParameters = new LinkedHashMap<>();
        final String requestQuery = requestUri.getRawQuery();
        if (requestQuery != null) {
            final String[] rawRequestParameters = requestQuery.split("[&;]", -1);
            for (final String rawRequestParameter : rawRequestParameters) {
                final String[] requestParameter = rawRequestParameter.split("=", 2);
                final String requestParameterName = decodeUrlComponent(requestParameter[0]);
                requestParameters.putIfAbsent(requestParameterName, new ArrayList<>());
                final String requestParameterValue = requestParameter.length > 1 ? decodeUrlComponent(requestParameter[1]) : null;
                requestParameters.get(requestParameterName).add(requestParameterValue);
            }
        }
        return requestParameters;
    }

    private static String decodeUrlComponent(final String urlComponent) {
        try {
            return URLDecoder.decode(urlComponent, CHARSET.name());
        } catch (final UnsupportedEncodingException ex) {
            throw new InternalError(ex);
        }
    }
}

附带说明,['hello world!'] 是无效的 JSON。字符串必须用双引号括起来。

【讨论】:

  • 注意:httpserver 包在 Eclipse 中似乎是“受限 API”。要规避,请参阅here
【解决方案2】:

你可以:

安装 Apache Tomcat,然后将一个 JSP 放到实现它的 ROOT 项目中。

我第二个@xehpuk。仅使用标准 Java 编写您自己的单类 HTTP 服务器实际上并不难。如果您想在早期版本中执行此操作,您可以使用 NanoHTTPD,这是一个非常知名的单类 HTTP 服务器实现。

我个人建议您研究一下 Apache Sling(几乎是 Java REST api 的参考实现)。您可以使用 Sling 在此处实现您的要求,而无需任何编程。

但正如其他人所建议的那样,执行此操作的标准方法是创建 java WAR 并将其部署到“servlet 容器”中,例如 Tomcat 或 Jetty 等。

【讨论】:

    【解决方案3】:

    如果您已经熟悉 servlet,您不需要太多创建一个简单的服务器来实现您想要的。但我想强调的是,您的需求可能会迅速增加,因此您可能需要转向 RESTful 框架(例如:Spring WS、Apache CXF)。

    您需要使用标准 servlet 技术注册 URI 并获取参数。也许你可以从这里开始:http://docs.oracle.com/cd/E13222_01/wls/docs92/webapp/configureservlet.html

    接下来,您需要一个 JSON 提供程序并将其序列化(也称为编组)为 JSON 格式。我推荐杰克逊。看看这个教程: http://www.sivalabs.in/2011/03/json-processing-using-jackson-java-json.html

    最后,您的代码将如下所示:

    public class Func1Servlet extends HttpServlet {
      public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    
        String p1 = req.getParameter("param1");
        String p2 = req.getParameter("param2");
    
        // Do Something with the params
    
        ResponseJSON resultJSON = new ResponseJSON();
        resultJSON.setProperty1(yourPropert1);
        resultJSON.setProperty2(yourPropert2);
    
        // Convert your JSON object into JSON string
        Writer strWriter = new StringWriter();
        mapper.writeValue(strWriter, resultJSON);
        String resultString = strWriter.toString();
    
        resp.setContentType("application/json");
        out.println(resultString );
      }
    }
    

    在您的 web.xml 中映射 URL:

    <servlet>
      <servlet-name>func1Servlet</servlet-name>
      <servlet-class>myservlets.func1servlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>func1Servlet</servlet-name>
      <url-pattern>/func1/*</url-pattern>
    </servlet-mapping>
    

    请记住,这是一个伪代码。您可以做很多事情来增强它,添加一些实用程序类等......

    尽管如此,随着项目的增长,您对更全面的框架的需求变得越来越明显。

    【讨论】:

    • 感谢您的回答。我如何在您的代码中定义它只会为func1 调用?如果我接到func2 的电话,我想以不同的方式回应
    • 一种方法,将 func1 映射到一个类,将 func2 映射到另一个类...还有很多其他方法,但这是最简单的。
    【解决方案4】:

    运行main以在端口8080上启动服务器

    public class Main {
        public static void main(String[] args) throws LifecycleException {
            Tomcat tomcat = new Tomcat();
            Context context = tomcat.addContext("", null);
    
            Tomcat.addServlet(context, "func1", new HttpServlet() {
                protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
                    Object response = func1(req.getParameter("param1"), req.getParameter("param2"));
    
                    ObjectMapper mapper = new ObjectMapper();
                    mapper.writeValue(resp.getWriter(), response);
                }
            });
            context.addServletMappingDecoded("/func1", "func1");
    
            tomcat.start();
            tomcat.getServer().await();
        }
    
        private static String[] func1(String p1, String p2) {
            return new String[] { "hello world", p1, p2 };
        }
    }
    

    Gradle 依赖项:

    dependencies {
        compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.28' // doesn't work with tomcat 9
        compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.4'
    }
    

    【讨论】:

      猜你喜欢
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 2014-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-29
      相关资源
      最近更新 更多