且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Servlet 中的 doGet 和 doPost

更新时间:2023-12-04 14:19:40

简介

您应该使用 doGet() 当你想拦截 HTTP GET 请求.您应该使用 doPost() 当你想拦截 HTTP POST 请求.就这样.不要将一个移植到另一个,反之亦然(例如在 Netbeans 不幸的自动生成的 processRequest() 方法中).这完全没有意义.

Introduction

You should use doGet() when you want to intercept on HTTP GET requests. You should use doPost() when you want to intercept on HTTP POST requests. That's all. Do not port the one to the other or vice versa (such as in Netbeans' unfortunate auto-generated processRequest() method). This makes no utter sense.

通常,HTTP GET 请求是幂等.IE.每次执行请求时都会得到完全相同的结果(将授权/身份验证和页面的时间敏感特性——搜索结果、最新消息等——排除在外).我们可以讨论一个可收藏的请求.单击链接、单击书签、在浏览器地址栏中输入原始 URL 等都会触发 HTTP GET 请求.如果 Servlet 正在侦听有问题的 URL,则将调用其 doGet() 方法.它通常用于预处理请求.IE.在呈现 JSP 的 HTML 输出之前先做一些业务工作,例如收集数据以显示在表格中.

Usually, HTTP GET requests are idempotent. I.e. you get exactly the same result everytime you execute the request (leaving authorization/authentication and the time-sensitive nature of the page —search results, last news, etc— outside consideration). We can talk about a bookmarkable request. Clicking a link, clicking a bookmark, entering raw URL in browser address bar, etcetera will all fire a HTTP GET request. If a Servlet is listening on the URL in question, then its doGet() method will be called. It's usually used to preprocess a request. I.e. doing some business stuff before presenting the HTML output from a JSP, such as gathering data for display in a table.

@WebServlet("/products")
public class ProductsServlet extends HttpServlet {

    @EJB
    private ProductService productService;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Product> products = productService.list();
        request.setAttribute("products", products); // Will be available as ${products} in JSP
        request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response);
    }

}

请注意,JSP 文件明确放置在 /WEB-INF 文件夹中,以防止最终用户能够在不调用预处理 servlet 的情况下直接访问它(因此最终会因看到空表).

Note that the JSP file is explicitly placed in /WEB-INF folder in order to prevent endusers being able to access it directly without invoking the preprocessing servlet (and thus end up getting confused by seeing an empty table).

<table>
    <c:forEach items="${products}" var="product">
        <tr>
            <td>${product.name}</td>
            <td><a href="product?id=${product.id}">detail</a></td>
        </tr>
    </c:forEach>
</table>

还查看/编辑上面最后一列中显示的详细信息链接通常是幂等的.

Also view/edit detail links as shown in last column above are usually idempotent.

@WebServlet("/product")
public class ProductServlet extends HttpServlet {

    @EJB
    private ProductService productService;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Product product = productService.find(request.getParameter("id"));
        request.setAttribute("product", product); // Will be available as ${product} in JSP
        request.getRequestDispatcher("/WEB-INF/product.jsp").forward(request, response);
    }

}

<dl>
    <dt>ID</dt>
    <dd>${product.id}</dd>
    <dt>Name</dt>
    <dd>${product.name}</dd>
    <dt>Description</dt>
    <dd>${product.description}</dd>
    <dt>Price</dt>
    <dd>${product.price}</dd>
    <dt>Image</dt>
    <dd><img src="productImage?id=${product.id}" /></dd>
</dl>

发布

HTTP POST 请求不是幂等的.如果最终用户事先在未执行重定向的 URL 上提交了 POST 表单,则该 URL 不一定可添加书签.提交的表单数据不会反映在 URL 中.将 URL 复制粘贴到新的浏览器窗口/选项卡中可能不一定会产生与表单提交后完全相同的结果.这样的 URL 是不可添加书签的.如果一个 Servlet 正在监听有问题的 URL,那么它的 doPost() 将被调用.它通常用于后处理请求.IE.从提交的 HTML 表单中收集数据并用它做一些业务(转换、验证、保存在数据库中等).最后,结果通常以来自转发的 JSP 页面的 HTML 形式呈现.

POST

HTTP POST requests are not idempotent. If the enduser has submitted a POST form on an URL beforehand, which hasn't performed a redirect, then the URL is not necessarily bookmarkable. The submitted form data is not reflected in the URL. Copypasting the URL into a new browser window/tab may not necessarily yield exactly the same result as after the form submit. Such an URL is then not bookmarkable. If a Servlet is listening on the URL in question, then its doPost() will be called. It's usually used to postprocess a request. I.e. gathering data from a submitted HTML form and doing some business stuff with it (conversion, validation, saving in DB, etcetera). Finally usually the result is presented as HTML from the forwarded JSP page.

<form action="login" method="post">
    <input type="text" name="username">
    <input type="password" name="password">
    <input type="submit" value="login">
    <span class="error">${error}</span>
</form>

...可以和这段Servlet结合使用:

...which can be used in combination with this piece of Servlet:

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    @EJB
    private UserService userService;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userService.find(username, password);

        if (user != null) {
            request.getSession().setAttribute("user", user);
            response.sendRedirect("home");
        }
        else {
            request.setAttribute("error", "Unknown user, please try again");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }

}

你看,如果 User 在 DB 中找到(即用户名和密码有效),那么 User 将被放入会话范围(即记录in"),servlet 将重定向到某个主页(此示例转到 http://example.com/contextname/home),否则它将设置错误消息并将请求转发回相同的 JSP 页面,以便 ${error} 显示消息.

You see, if the User is found in DB (i.e. username and password are valid), then the User will be put in session scope (i.e. "logged in") and the servlet will redirect to some main page (this example goes to http://example.com/contextname/home), else it will set an error message and forward the request back to the same JSP page so that the message get displayed by ${error}.

如有必要,您还可以隐藏"/WEB-INF/login.jsp 中的 login.jsp 以便用户只能通过 servlet 访问它.这使 URL 保持干净http://example.com/contextname/login.您需要做的就是像这样向 servlet 添加一个 doGet():

You can if necessary also "hide" the login.jsp in /WEB-INF/login.jsp so that the users can only access it by the servlet. This keeps the URL clean http://example.com/contextname/login. All you need to do is to add a doGet() to the servlet like this:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
}

(并相应地更新 doPost() 中的同一行)

(and update the same line in doPost() accordingly)

也就是说,我不确定它是否只是在黑暗中玩耍和射击,但是您发布的代码看起来不太好(例如使用 compareTo() 而不是 equals() 并在参数名中挖掘而不是仅使用 getParameter()idpassword 似乎是声明为 servlet 实例变量——这不是 线程安全).因此,我强烈建议您使用 Oracle 教程了解更多有关基本 Java SE API 的信息(查看涵盖基础知识的路径"一章)以及如何使用 那些教程.

That said, I am not sure if it is just playing around and shooting in the dark, but the code which you posted doesn't look good (such as using compareTo() instead of equals() and digging in the parameternames instead of just using getParameter() and the id and password seems to be declared as servlet instance variables — which is NOT threadsafe). So I would strongly recommend to learn a bit more about basic Java SE API using the Oracle tutorials (check the chapter "Trails Covering the Basics") and how to use JSP/Servlets the right way using those tutorials.

更新:根据您问题的更新(这是非常重要的,您不应该删除原始问题的部分内容,这会使答案变得毫无价值......而是添加em> 新块中的信息),事实证明您不必要地将表单的编码类型设置为 multipart/form-data.这将以与(默认)application/x-www-form-urlencoded 不同的组合发送请求参数,后者将请求参数作为查询字符串发送(例如 name1=value1&name2=value2&name3=value3).只要表单中有 <input type="file"> 元素,您就只需要 multipart/form-data 来上传可能是非字符的文件数据(二进制数据).您的情况并非如此,因此只需将其删除,它就会按预期工作.如果您需要上传文件,那么您必须设置编码类型并自己解析请求正文.通常你使用 Apache Commons FileUpload 那里,但如果你已经在全新的 Servlet 3.0 API,那么您可以使用以 HttpServletRequest#getPart().有关具体示例,另请参阅此答案:How使用 JSP/Servlet 上传文件到服务器?

Update: as per the update of your question (which is pretty major, you should not remove parts of your original question, this would make the answers worthless .. rather add the information in a new block) , it turns out that you're unnecessarily setting form's encoding type to multipart/form-data. This will send the request parameters in a different composition than the (default) application/x-www-form-urlencoded which sends the request parameters as a query string (e.g. name1=value1&name2=value2&name3=value3). You only need multipart/form-data whenever you have a <input type="file"> element in the form to upload files which may be non-character data (binary data). This is not the case in your case, so just remove it and it will work as expected. If you ever need to upload files, then you'll have to set the encoding type so and parse the request body yourself. Usually you use the Apache Commons FileUpload there for, but if you're already on fresh new Servlet 3.0 API, then you can just use builtin facilities starting with HttpServletRequest#getPart(). See also this answer for a concrete example: How to upload files to server using JSP/Servlet?