1. 概述
對於最終用户而言,表單提交的過程就像輸入數據並點擊提交按鈕一樣便捷。然而,從工程角度來看,它需要一個編碼機制,以可靠地將客户端的數據發送到服務器端進行後端處理。
在本教程的範圍內,我們將重點創建發送其數據為 <em >application/x-www-form-urlencoded</em > 內容類型的表單,在 Spring Web 應用程序中。
2. 表單數據編碼
使用最常見的 HTTP 方法提交表單數據是 POST。然而,對於冪等表單提交,我們也可以使用 HTTP GET 方法。並且,通過 <a href="https://www.w3.org/TR/html4/interact/forms.html#adef-method">表單的 method 屬性</a> 來指定方法。
對於使用 GET 方法的表單,整個表單數據將作為查詢字符串的一部分發送。但是,如果使用 POST 方法,則數據將作為 HTTP 請求的主體發送。
此外,在後者中,我們還可以通過表單的 <a href="https://www.w3.org/TR/html4/interact/forms.html#adef-enctype"><em >enctype 屬性</em></a > 指定數據的編碼,它可以取兩個值,即application/x-www-form-urlencoded 及其 multipart/form-data。
2.1. 媒體類型 application/x-www-form-urlencoded
HTML 表單的默認值為 application/x-www-form-urlencoded,用於 enctype 屬性,因為這處理了基本用例,即數據完全為文本。然而,如果我們的用例涉及支持文件數據,則必須將其覆蓋為 multipart/form-data。
基本上,它將表單數據作為鍵值對發送,鍵值對之間用 "&" 字符分隔。 此外,每個鍵和值都用 "=" 字符分隔。 進一步,所有保留字符和非字母數字字符都使用 百分編碼 進行編碼。
3. 瀏覽器表單提交
現在我們已經掌握了基本內容,接下來讓我們看看如何在 Spring Web 應用中處理 URL 編碼的表單數據,用於一個簡單的反饋提交用例。
3.1. 領域模型
為了我們的反饋表單,我們需要捕獲提交者的電子郵件標識符以及評論。因此,讓我們創建一個 領域模型,在 反饋 類中:
public class Feedback {
private String emailId;
private String comment;
}3.2. 創建表單
為了使用簡單的 HTML 模板創建我們的動態 Web 表單,我們需要在項目中配置 Thymeleaf。之後,我們準備添加一個 GET 端點 /feedback,該端點將為表單提供 feedback 視圖:
@GetMapping(path = "/feedback")
public String getFeedbackForm(Model model) {
Feedback feedback = new Feedback();
model.addAttribute("feedback", feedback);
return "feedback";
}請注意,我們使用 反饋 作為模型屬性來捕獲用户輸入。接下來,讓我們在 創建 反饋 視圖,在 feedback.html 模板中:
<form action="#" method="post" th:action="@{/web/feedback}" th:object="${feedback}">
<!-- form fields for feedback's submitter and comment info -->
</form>當然,我們無需明確指定 enctype 屬性,因為它會選擇默認值 application/x-www-form-urlencoded。
3.3. PRG 流程
當通過瀏覽器反饋表單接收用户輸入時,必須實施 POST/REDIRECT/GET (PRG) 提交工作流程,以避免重複提交。
首先,讓我們實施 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";
}接下來,我們可以實現重定向端點 /feedback/success</em/>,該端點處理 GET 請求:
@GetMapping("/feedback/success")
public ResponseEntity<String> getSuccess() {
return new ResponseEntity<String>("Thank you for submitting feedback.", HttpStatus.OK);
}為了驗證瀏覽器中表單提交工作流的功能,請訪問 localhost:8080/feedback:
最後,我們還可以檢查表單數據是否以 URL 編碼的形式發送:
emailId=abc%40example.com&comment=Sample+Feedback4. 非瀏覽器請求
在某些情況下,我們可能沒有基於瀏覽器的 HTTP 客户端。 我們的客户端可能是一個實用工具,例如 cURL 或 Postman。 在這種情況下,我們不需要 HTML Web 表單。 相反,我們可以實現一個 /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);
}在沒有 HTML 表單的情況下,我們的數據流中並不一定需要實現 PRG 模式。但是,我們必須明確指定該資源接受 APPLICATION_FORM_URLENCODED_VALUE 媒體類型。
最後,我們可以使用 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. <em >FormHttpMessageConverter</em> 基礎
一個發送 <em >application/x-www-form-urlencoded</em> 數據的 HTTP 請求,必須在 <em >Content-Type</em> 頭部中指定。 內部,Spring 使用 <em ><a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/FormHttpMessageConverter.html">FormHttpMessageConverter</a></em> 類來讀取此數據並將其與方法參數綁定。
當我們的方法參數類型為 <em >MultiValueMap</em> 時,我們可以使用 <em ><a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html">@RequestParam</a></em> 註解或 <em ><a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html">@RequestBody</a></em> 註解,以便將其與 HTTP 請求體正確地綁定。 這是因為 Servlet API 將查詢參數和表單數據合併到一個名為 <em >parameters</em> 的單個映射中,並且它還包括對請求體自動解析:
@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);
}然而,對於非 MultiValueMap 類型的方法參數,例如我們的 Feedback 領域對象,我們必須僅使用 @RequestBody 註解。
5. 結論
在本教程中,我們簡要學習了 Web 表單中表單數據的編碼方式。我們還探討了如何處理 URL 編碼的數據,用於瀏覽器和非瀏覽器 HTTP 請求,並通過在 Spring Boot Web 應用中實現一個反饋表單來演示。