Handling URL Encoded Form Data in Spring REST – 在Spring REST中处理URL编码的表单数据

最后修改: 2019年 12月 2日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

For an end-user, the process of form submission is convenient, and to some extent, equivalent to just entering data and clicking on a submit button. However, from an engineering perspective, it takes an encoding mechanism to reliably send and receive this data from the client-side to the server-side for back-end processing.

对于终端用户来说,表单的提交过程是很方便的,在某种程度上,相当于只是输入数据和点击提交按钮。然而,从工程的角度来看,它需要一个编码机制来可靠地发送和接收从客户端到服务器端的这些数据,以便进行后端处理。

For the scope of this tutorial, we’ll focus on creating a form that sends its data as application/x-www-form-urlencoded content type in a Spring web application.

在本教程的范围内,我们将专注于在Spring Web应用程序中创建一个以application/x-www-form-urlencoded内容类型发送其数据的表单

2. Form Data Encoding

2.表格数据编码

The most commonly used HTTP method for form submissions is POST. However, for idempotent form submissions, we can also use the HTTP GET method. And, the way to specify the method is through the form’s method attribute.

提交表单最常用的HTTP方法是POST。但是,对于idempotent表单提交,我们也可以使用HTTP GET方法。而且,指定方法的方式是通过表单的方法属性

For forms that use the GET method, the entire form data is sent as part of the query string. But, if we’re using the POST method, then its data is sent as part of the body of the HTTP request.

对于使用GET方法的表单,整个表单数据被作为查询字符串的一部分发送。但是,如果我们使用POST方法,那么它的数据就作为HTTP请求正文的一部分被发送。

Moreover, in the latter case, we can also specify the encoding of data with the form’s enctype attribute, which can take two values, namely application/x-www-form-urlencoded and multipart/form-data.

此外,在后一种情况下,我们还可以用表单的enctype属性指定数据的编码,它可以取两个值,即application/x-www-form-urlencodedmultipart/form-data

2.1. Media Type application/x-www-form-urlencoded

2.1.媒体类型application/x-www-form-urlencoded

HTML forms have a default value of application/x-www-form-urlencoded for the enctype attribute as this takes care of the basic use cases where data is entirely text. Nevertheless, if our use case involves supporting file data, then we’ll have to override it with a value of multipart/form-data.

HTML表单的enctype属性的默认值为application/x-www-form-urlencoded,因为这可以照顾到数据完全是文本的基本用例。然而,如果我们的用例涉及支持文件数据,那么我们就必须用multipart/form-data的值来覆盖它。

Essentially, it sends the form data as key-value pairs separated by an ampersand (&) character. Also, the respective key and value are separated with the equals sign (=). Further, all reserved and non-alphanumeric characters are encoded using percent-encoding.

从本质上讲,它以键值对的形式发送表单数据,并以安培号(&)字符分隔。另外,各自的键和值都用等号(=)分开。此外,所有保留和非字母数字字符都使用percent-encoding进行编码。

3. Form Submission in Browser

3.在浏览器中提交表格

Now that we have our basics covered, let’s go ahead and see how we can handle URL encoded form data for a simple use case of feedback submission in a Spring web app.

现在我们已经掌握了基础知识,让我们继续看看如何在Spring Web应用中处理URL编码的表单数据,以实现反馈提交这一简单的使用案例

3.1. Domain Model

3.1.领域模型

For our feedback form, we need to capture the email identifier of the submitter along with the comment. So, let’s create our domain model in a Feedback class:

对于我们的反馈表,我们需要捕获提交者的电子邮件标识符和评论。因此,让我们在一个Feedback类中创建我们的域模型

public class Feedback {
    private String emailId;
    private String comment;
}

3.2. Create Form

3.2.创建表格

To use a simple HTML template to create our dynamic web form, we’ll need to configure Thymeleaf in our project. After this, we’re ready to add a GET endpoint /feedback that will serve the feedback view for the form:

为了使用简单的HTML模板来创建我们的动态Web表单,我们需要在项目中配置Thymeleaf。在此之后,我们准备添加一个GET端点/feedback,它将为表单提供feedback视图

@GetMapping(path = "/feedback")
public String getFeedbackForm(Model model) {
    Feedback feedback = new Feedback();
    model.addAttribute("feedback", feedback);
    return "feedback";
}

Note that we’re using feedback as a model attribute to capture the user input. Next, let’s create the feedback view in the feedback.html template:

注意,我们使用feedback作为模型属性来捕获用户的输入。接下来,让我们在feedback.html模板中创建feedback视图。

<form action="#" method="post" th:action="@{/web/feedback}" th:object="${feedback}">
    <!-- form fields for feedback's submitter and comment info -->
</form>

Of course, we don’t need to explicitly specify the enctype attribute as it’ll pick the default value of application/x-www-form-urlencoded.

当然,我们不需要明确指定enctype属性,因为它将选择默认值application/x-www-form-urlencoded

3.3. PRG Flow

3.3.PRG流量

As we’re accepting user input through the browser feedback form, we must implement the POST/REDIRECT/GET (PRG) submission workflow to avoid duplicate submissions.

由于我们通过浏览器反馈表接受用户输入,我们必须实现POST/REDIRECT/GET(PRG)提交工作流程以避免重复提交

First, let’s implement the POST endpoint /web/feedback that’ll act as the action handler for the feedback form:

首先,让我们实现POST端点/web/feedback,它将作为反馈表单的动作处理程序。

@PostMapping(
  path = "/web/feedback",
  consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public String handleBrowserSubmissions(Feedback feedback) throws Exception {
    // Save feedback data
    return "redirect:/feedback/success";
}

Next, we can implement the redirect endpoint /feedback/success that serves a GET request:

接下来,我们可以实现重定向端点/feedback/success,提供GET请求。

@GetMapping("/feedback/success")
public ResponseEntity<String> getSuccess() {
    return new ResponseEntity<String>("Thank you for submitting feedback.", HttpStatus.OK);
}

To validate the functionality of form submission workflow in a browser, let’s visit localhost:8080/feedback:

为了在浏览器中验证表单提交工作流程的功能,让我们访问localhost:8080/feedback

Finally, we can also inspect that form data is being sent in the URL encoded form:

最后,我们还可以检查表单数据是否以URL编码的形式发送。

emailId=abc%40example.com&comment=Sample+Feedback

4. Non-Browser Requests

4.非浏览器请求

At times, we might not have a browser-based HTTP client. Instead, our client could be a utility such as cURL or Postman. In such a case, we don’t need the HTML web form. Instead, we can implement a /feedback endpoint that serves the POST request:

有时,我们可能没有一个基于浏览器的HTTP客户端。相反,我们的客户端可能是一个实用程序,如cURLPostman。在这种情况下,我们不需要HTML网页表单。相反,我们可以实现一个/feedback端点,提供POST请求。

@PostMapping(
  path = "/feedback",
  consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public ResponseEntity<String> handleNonBrowserSubmissions(@RequestBody Feedback feedback) throws Exception {
    // Save feedback data
    return new ResponseEntity<String>("Thank you for submitting feedback", HttpStatus.OK);
}

In the absence of the HTML form in our data flow, we don’t necessarily need to implement the PRG pattern. However, we must specify that the resource accepts APPLICATION_FORM_URLENCODED_VALUE media type.

在我们的数据流中没有HTML表单的情况下,我们不一定需要实现PRG模式。然而,我们必须指定该资源接受APPLICATION_FORM_URLENCODED_VALUE 媒体类型

Finally, we can test it with a cURL request:

最后,我们可以用cURL请求来测试它。

curl -X POST \
  http://localhost:8080/feedback \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'emailId=abc%40example.com&comment=Sample%20Feedback'

4.1. FormHttpMessageConverter Basics

4.1.FormHttpMessageConverter基础知识

An HTTP request that sends application/x-www-form-urlencoded data must specify this in the Content-Type header. Internally, Spring uses the FormHttpMessageConverter class to read this data and bind it with the method parameter.

发送application/x-www-form-urlencoded数据的HTTP请求必须在Content-Type头中指定。在内部,Spring使用FormHttpMessageConverter类来读取这些数据并将其与方法参数绑定。

In cases where our method parameter is of a type MultiValueMap, we can use either the @RequestParam or @RequestBody annotation to bind it appropriately with the body of the HTTP request. That’s because the Servlet API combines the query parameters and form data into a single map called parameters, and that includes automatic parsing of the request body:

在我们的方法参数属于MultiValueMap类型的情况下,我们可以使用@RequestParam@RequestBody注解,将其与HTTP请求的主体适当地绑定。这是因为Servlet API将查询参数和表单数据合并为一个名为parameters的地图,这包括请求主体的自动解析。

@PostMapping(
  path = "/feedback",
  consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public ResponseEntity<String> handleNonBrowserSubmissions(
  @RequestParam MultiValueMap<String,String> paramMap) throws Exception {
    // Save feedback data
    return new ResponseEntity<String>("Thank you for submitting feedback", HttpStatus.OK);
}

However, for a method parameter of type other than MultiValueMap, such as our Feedback domain object, we must use only the @RequestBody annotation.

然而,对于MultiValueMap以外类型的方法参数,例如我们的Feedback域对象,我们必须只使用@RequestBody注释。

5. Conclusion

5.总结

In this tutorial, we briefly learned about the encoding of form data in web forms. We also explored how to handle URL encoded data for browser and non-browser HTTP requests by implementing a feedback form in a Spring Boot web app.

在本教程中,我们简要地了解了Web表单中表单数据的编码问题。我们还通过在Spring Boot网络应用中实现一个反馈表单,探索了如何处理浏览器和非浏览器HTTP请求的URL编码数据。

As always, the complete source code for the tutorial is available over on GitHub.

一如既往,该教程的完整源代码可在GitHub上获得over