【问题标题】:Jetty is inexplicably eating POST parametersJetty莫名其妙吃POST参数
【发布时间】:2013-12-18 19:58:55
【问题描述】:

我正在运行嵌入式 Jetty (9.1.0.v20131115) 设置,并设置了多个处理程序来处理多个不同上下文中的请求。

当用户提交表单时,其中一个处理程序执行登录功能。设置如下:

ContextHandler loginContext = new ContextHandler("/login");
loginContext.setHandler(new LoginHandler());

// Other handlers go here...

contexts.setHandlers(new Handler[]{rootContext, logoutContext, loginContext, resourceHandler});
server.setHandler(contexts);

这应该是很标准的,没什么特别的。令我困惑的是,当我通过调试器运行 LoginHandler 时,HttpServletRequest 对象没有参数,即使表单明明有两个表单输入元素!

这是我通过 netcat 捕获的请求的副本:

POST /login HTTP/1.1
Host: localhost:52520
Connection: keep-alive
Content-Length: 31
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://localhost:52520
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:52520/dashboard/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

username=myuser&password=mypass

最重要的是,如果我将表单操作更改为 GET 而不是 POST,则参数显示得很好!

要让 Handler 接受 POST 参数,有什么特别需要做的吗?

【问题讨论】:

    标签: jetty embedded-jetty


    【解决方案1】:

    似乎工作得很好。

    package jetty;
    
    import static org.hamcrest.CoreMatchers.*;
    import static org.junit.Assert.*;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.io.StringReader;
    import java.io.StringWriter;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.server.Request;
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.ServerConnector;
    import org.eclipse.jetty.server.handler.ContextHandler;
    import org.eclipse.jetty.server.handler.DefaultHandler;
    import org.eclipse.jetty.server.handler.HandlerCollection;
    import org.eclipse.jetty.server.handler.HandlerWrapper;
    import org.eclipse.jetty.util.IO;
    import org.junit.AfterClass;
    import org.junit.BeforeClass;
    import org.junit.Test;
    
    public class JettyPostTest
    {
        public static class LoginHandler extends HandlerWrapper
        {
            @Override
            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
            {
                PrintWriter out = response.getWriter();
                response.setContentType("text/plain");
    
                out.printf("username = %s\n",request.getParameter("username"));
                out.printf("password = %s\n",request.getParameter("password"));
    
                baseRequest.setHandled(true);
            }
        }
    
        private static Server server;
        private static int port;
    
        @BeforeClass
        public static void startServer() throws Exception
        {
            server = new Server();
            ServerConnector connector = new ServerConnector(server);
            connector.setPort(0);
            server.addConnector(connector);
    
            // collection for handlers
            HandlerCollection handlers = new HandlerCollection();
            server.setHandler(handlers);
    
            // login context
            ContextHandler loginContext = new ContextHandler("/login");
            loginContext.setHandler(new LoginHandler());
            handlers.addHandler(loginContext);
    
            // default handler
            handlers.addHandler(new DefaultHandler());
    
            // start server
            server.start();
    
            // grab port
            port = connector.getLocalPort();
        }
    
        @AfterClass
        public static void stopServer() throws Exception
        {
            server.stop();
        }
    
        @Test
        public void testPostParameters() throws IOException
        {
            StringBuilder req = new StringBuilder();
            req.append("POST /login/ HTTP/1.1\r\n");
            req.append("Host: localhost:").append(port).append("\r\n");
            req.append("Connection: close\r\n");
            req.append("Content-Length: 31\r\n");
            req.append("Cache-Control: max-age=0\r\n");
            req.append("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n");
            req.append("Origin: http://localhost:").append(port).append("\r\n");
            req.append("User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36\r\n");
            req.append("Content-Type: application/x-www-form-urlencoded\r\n");
            req.append("Referer: http://localhost:").append(port).append("/dashboard/\r\n");
            req.append("Accept-Encoding: gzip,deflate,sdch\r\n");
            req.append("Accept-Language: en-US,en;q=0.8\r\n");
            req.append("\r\n");
            req.append("username=myuser&password=mypass\r\n");
    
            try (Socket socket = new Socket())
            {
                socket.connect(new InetSocketAddress("localhost",port));
                // Write request
                try (OutputStream out = socket.getOutputStream();
                        OutputStreamWriter writer = new OutputStreamWriter(out);
                        InputStream in = socket.getInputStream();
                        InputStreamReader reader = new InputStreamReader(in))
                {
                    StringReader reqStream = new StringReader(req.toString());
                    IO.copy(reqStream,writer);
                    writer.flush();
                    out.flush();
    
                    StringWriter respStream = new StringWriter();
                    IO.copy(reader,respStream);
    
                    System.out.println(respStream.toString());
    
                    String expected = "username = myuser\npassword = mypass\n";
                    assertThat("Response",respStream.toString(),containsString(expected));
                }
            }
        }
    }
    

    输出结果:

    2013-12-18 13:23:08.856:INFO:oejs.Server:main: jetty-9.1.0.v20131115
    2013-12-18 13:23:08.888:INFO:oejsh.ContextHandler:main: Started o.e.j.s.h.ContextHandler@49ada86{/login,null,AVAILABLE}
    2013-12-18 13:23:08.897:INFO:oejs.ServerConnector:main: Started ServerConnector@3f14b553{HTTP/1.1}{0.0.0.0:34456}
    HTTP/1.1 200 OK
    Content-Type: text/plain; charset=ISO-8859-1
    Connection: close
    Server: Jetty(9.1.0.v20131115)
    
    username = myuser
    password = mypass
    
    2013-12-18 13:23:08.994:INFO:oejs.ServerConnector:main: Stopped ServerConnector@3f14b553{HTTP/1.1}{0.0.0.0:0}
    2013-12-18 13:23:08.995:INFO:oejsh.ContextHandler:main: Stopped o.e.j.s.h.ContextHandler@49ada86{/login,null,UNAVAILABLE}
    

    我根据您的请求所做的唯一更改是将 Connection: keep-alive 更改为 Connection: close 以让码头关闭连接。此更改很小,仅用于让测试快速执行,使用原始值不会更改测试结果。

    您的问题中缺少某些信息。

    【讨论】:

    • 确实,你是对的。运行你的单元测试我看到了相同的结果。我现在正在经历这些差异......
    • Joakim,您进行了另一项未提及的更改。您似乎已将“POST /login HTTP/1.1”更改为“POST /login/ HTTP/1.1”,尾部的斜杠使一切变得不同。
    • 啊,是的。对 "/login" 的请求将导致 302 重定向到 "/login/",并且您的客户端可能无法正确重新提交 POST。我只是跳过了 302 重定向并让测试执行重定向后的请求。
    猜你喜欢
    • 2012-05-19
    • 1970-01-01
    • 2012-03-12
    • 2019-02-04
    • 2021-08-19
    • 2013-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多