1. 概述
對於最終用户而言,表單提交的過程非常便捷,在某種程度上也相當於直接輸入數據並點擊提交按鈕。但是,從工程角度來看,它需要一個編碼機制,以可靠地將客户端的數據發送到服務器端進行後端處理。
在本教程的範圍內,我們將重點關注創建發送其數據為<em>application/x-www-form-urlencoded</em>內容類型的表單,該表單將在 Spring Web 應用程序中實現。
2. 表單數據編碼
提交表單最常用的 HTTP 方法是 POST。但是,對於冪等表單提交,我們也可以使用 HTTP GET 方法。並且,指定方法的途徑是通過表單的 method 屬性。
對於使用 GET 方法的表單,整個表單數據將作為查詢字符串的一部分發送。但是,如果使用 POST 方法,則數據將作為 HTTP 請求的主體發送。
此外,在後者的情況下,我們還可以通過表單的 enctype 屬性, 來指定數據的編碼方式,它可以取兩個值,即 application/x-www-form-urlencoded 和 multipart/form-data.
2.1. 媒體類型 application/x-www-form-urlencoded
HTML 表單的默認值對於 enctype 屬性是 application/x-www-form-urlencoded,因為這處理了基本用例,其中數據完全是文本。儘管如此,如果我們的用例涉及支持文件數據,則我們需要使用 multipart/form-data 的值來覆蓋它。
本質上,它將表單數據作為用鍵值對分隔,並用感嘆號 (&) 分隔。相應地,鍵和值用等號 (=) 分隔。此外,所有保留字符和非字母數字字符都使用 百分編碼 進行編碼。
3. 形式提交到瀏覽器
現在我們已經完成了基礎知識,讓我們看看如何處理 URL 編碼的表單數據,用於一個簡單的反饋提交用例,在 Spring Web 應用程序中。
3.1. 域模型
對於我們的反饋表單,我們需要捕獲提交者的電子郵件標識符以及評論。因此,讓我們在 反饋 類中創建我們的域模型:
public class Feedback {
private String emailId;
private String comment;
}
3.2. 創建表單
為了使用簡單的 HTML 模板來創建我們的動態 Web 表單,我們需要在項目中使用 Thymeleaf。之後,我們準備好添加一個 GET 端點/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,它提供一個 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+Feedback
4. 非瀏覽器請求
有時,我們可能沒有基於瀏覽器的 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 {
// 保存反饋數據
return new ResponseEntity<String>("感謝您提交反饋", 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. FormHttpMessageConverter 基礎
發送 application/x-www-form-urlencoded 數據 的 HTTP 請求 必須在 Content-Type 標頭中指定此項。 內部,Spring 使用 FormHttpMessageConverter 類來讀取此數據並將其與方法參數綁定。
在我們的方法參數類型為 @RequestParam 或 @RequestBody 類型的 경우, 我們可以使用這些註解來相應地將其與 HTTP 請求的 body 綁定。 這是因為 Servlet API 將查詢參數和表單數據組合成一個名為 parameters 的單個 map,並且它會自動解析請求 body:
@PostMapping(
path = "/feedback",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public ResponseEntity<String> handleNonBrowserSubmissions(
@RequestParam MultiValueMap<String,String> paramMap) throws Exception {
// 保存反饋數據
return new ResponseEntity<String>("感謝您提交反饋", HttpStatus.OK);
}
但是,對於不是 MultiValueMap類型的 方法參數,例如我們的 Feedback 域對象,我們必須僅使用 @RequestBody 註解。
5. 結論
在本教程中,我們簡要了解了表單數據在 Web 表單中的編碼方式。我們還探討了如何通過在 Spring Boot Web 應用中實現反饋表單,來處理 URL 編碼的數據,用於瀏覽器和非瀏覽器 HTTP 請求。