從 Java Servlet 中的 POST 請求取得請求有效負載
一、簡介
從請求正文中有效提取有效負載資料對於 Java Servlet 至關重要,Java Servlet 可作為處理傳入 HTTP 請求的伺服器端元件。
本指南探討了在 Java servlet 中提取有效負載資料的各種方法,以及最佳實務和注意事項。
2. 了解請求負載
Post 請求主要用於透過HTTP請求向伺服器傳送資料。這些數據可以是任何內容,從包含使用者輸入的表單資料到 JSON 和 XML 等結構化數據,甚至二進位。這些資料駐留在請求正文中,與 URL 分開。這允許更廣泛和安全的資料傳輸。我們可以使用請求中的Content-Type標頭來識別不同類型的資料。
常見的內容類型包括:
-
application/x-www-form-urlencoded:用於編碼為鍵值對的表單數據 -
application/json:用於 JSON 格式的數據 -
application/xml:用於 XML 格式的數據 -
text/plain:用於發送純文本 -
multipart/form-data:用於上傳二進位檔案以及常規表單數據
3. 檢索 POST 有效負載資料的方法
讓我們探索從 POST 請求負載中提取資料的不同方法。
3.1.使用getParameter()取得Form-UrlEncoded數據
我們可以使用HttpServletRequest介面提供的getParameter()方法,透過 POST 請求提交的參數名稱來檢索特定的表單資料。它將參數名稱作為參數,並以String形式傳回相應的值。
讓我們用一個例子來說明這一點:
@WebServlet(name = "FormDataServlet", urlPatterns = "/form-data")
public class FormDataServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String firstName = StringEscapeUtils.escapeHtml4(req.getParameter("first_name"));
String lastName = StringEscapeUtils.escapeHtml4(req.getParameter("last_name"));
resp.getWriter()
.append("Full Name: ")
.append(firstName)
.append(" ")
.append(lastName);
}
}
此方法可以處理鍵值對,但不適合處理複雜的資料結構。
我們使用 apache commons 文字庫中StringEscapeUtils類別的escapeHtml4()方法透過編碼特殊字元來清理輸入。這有助於防止 XSS 攻擊。我們可以透過新增以下相依性來使用該函式庫:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
3.2.讀取原始有效負載字串
為了獲得更大的靈活性,我們可以使用HttpServletRequest介面中的getReader()方法存取原始有效負載資料。
該方法傳回一個BufferedReader對象,它允許我們逐行讀取資料:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
StringBuilder payload = new StringBuilder();
try(BufferedReader reader = req.getReader()){
String line;
while ((line = reader.readLine()) != null){
payload.append(line);
}
}
resp.getWriter().append(countWordsFrequency(payload.toString()).toString());
}
重要考慮因素:
- 處理大負載時我們必須小心以避免記憶體問題
- 我們可能需要根據請求處理字元編碼差異
3.3.解析結構化資料格式(JSON、XML)
JSON 和 XML 等結構化資料格式廣泛用於客戶端和伺服器之間交換資料。我們可以使用專用函式庫將有效負載解析為 Java 物件。
為了解析 JSON 數據,我們可以使用 Jackson 或 Gson 等流行的函式庫。在我們的範例中,我們將使用 Gson。為此,我們需要新增另一個依賴項:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
我們可以使用BufferedReader物件從請求正文中讀取 JSON 負載,將其獲取為純文本,然後我們可以使用 Gson 將其解析為 Java 物件。
下面是解析 JSON 資料的範例程式碼:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String contentType = req.getContentType();
if (!("application/json".equals(contentType))) {
resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
"Invalid content type");
return;
}
try (BufferedReader reader = req.getReader()) {
Gson gson = new Gson();
Product newProduct = gson.fromJson(reader, Product.class);
resp.getWriter()
.append("Added new Product with name: ")
.append(newProduct.getName());
} catch (IOException ex) {
req.setAttribute("message", "There was an error: " + ex.getMessage());
}
}
我們應該在解析之前始終驗證內容類型,以防止意外的資料格式和安全性問題。
為了解析 XML 數據,我們使用 XStream 函式庫。我們將在程式碼中使用以下依賴項:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.20</version>
</dependency>
要從請求正文中解析 XML,我們可以將其讀取為純文本,就像處理 JSON 負載一樣,然後使用 XStream 將其解析為 Java 物件。
以下是從 POST 請求解析 XML 負載的範例程式碼:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String contentType = req.getContentType();
if (!("application/xml".equals(contentType))) {
resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
"Invalid content type");
return;
}
try (BufferedReader reader = req.getReader()) {
XStream xStream = new XStream();
xStream.allowTypesByWildcard(new String[] { "com.baeldung.**" });
xStream.alias("Order", Order.class);
Order order = (Order) xStream.fromXML(reader);
resp.getWriter()
.append("Created new Order with orderId: ")
.append(order.getOrderId())
.append(" for Product: ")
.append(order.getProduct());
} catch (IOException ex) {
req.setAttribute("message", "There was an error: " + ex.getMessage());
}
}
3.4.處理multipart/form-data
在處理文件上傳時, multipart/form-data內容類型至關重要。它專門設計用於處理包含二進位資料(例如圖像、視訊或文件)以及常規文字資料的表單。
要處理multipart/form-data ,我們必須使用@MultipartConfig註解我們的 servlet 或在web.xml中設定 servlet 。
@MultipartConfig提供了各種參數來控製檔案上傳行為,例如location (暫存目錄)、 maxFileSize (單一上傳檔案的最大大小)、 maxRequestSize (整個請求的最大大小):
@MultipartConfig(fileSizeThreshold = 1024 * 1024,
maxFileSize = 1024 * 1024 * 5,
maxRequestSize = 1024 * 1024 * 5 * 5)
public class FileUploadServlet extends HttpServlet {
在 servlet 中,我們可以使用特定部分的getPart(String name)方法來檢索multipart/form-data請求的各個部分,或使用getParts()來檢索所有部分。 Part介面提供了存取檔案名稱、內容類型、大小和輸入流等詳細資訊的方法。
讓我們舉例說明使用 POST 請求負載上傳檔案的範例:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String uploadPath = getServletContext().getRealPath("") +
File.separator + UPLOAD_DIRECTORY;
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
Part filePart = request.getPart("file");
if (filePart != null) {
String fileName = Paths.get(filePart.getSubmittedFileName())
.getFileName().toString();
if(fileName.isEmpty()){
response.getWriter().println("Invalid File Name!");
return;
}
if(!fileName.endsWith(".txt")){
response.getWriter().println("Only .txt files are allowed!");
return;
}
File file = new File(uploadPath, fileName);
try (InputStream fileContent = filePart.getInputStream()) {
Files.copy(fileContent, file.toPath(),
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
response.getWriter().println("Error writing file: " +
e.getMessage());
return;
}
response.getWriter()
.println("File uploaded to: " + file.toPath());
} else {
response.getWriter()
.println("File upload failed!");
}
}
關鍵安全考量:
- 路徑遍歷保護
- 文件類型驗證
- 檔案大小限制
- 安全文件寫入
4. 最佳實務與常見陷阱
4.1. Content-Type驗證
我們應該始終驗證傳入請求的內容類型,以確保伺服器正確處理請求。這有助於防止意外的資料格式和潛在的安全漏洞。
例如,如果我們的 servlet 需要 JSON,我們應該在處理之前檢查Content-Type標頭的類型為application/json :
String contentType = req.getContentType();
if (!("application/json".equals(contentType))) {
resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
"Invalid content type");
return;
}
或者,我們可以使用 Apache Tika 做一些更強大的事情。
4.2.錯誤處理
在讀取和處理有效負載資料時,我們應該始終實施適當的錯誤處理。這確保了我們的應用程式可以優雅地處理意外情況。
我們還應該提供帶有 HTTP 狀態代碼的有意義的訊息,這可以幫助告訴開發人員和使用者出了什麼問題。
4.3.效能最佳化
處理非常大的有效負載會影響效能。為了最佳化,我們應該考慮限制傳入請求的大小、串流資料處理和避免資料複製。像 Apache Commons IO 這樣的函式庫有助於有效負載處理。
另外,我們應該確保 servlet 不執行阻塞操作,這可能會阻塞請求處理。
4.4.安全
處理 POST 請求負載時,安全性是一個重要的考慮因素。一些關鍵做法包括:
- 輸入驗證:始終驗證和清理輸入資料以防止注入攻擊
- 身份驗證和授權:確保只有授權使用者才能存取某些端點
- CSRF 保護:實施跨站點請求偽造 (CSRF) 令牌,以防止 Web 應用程式信任的使用者傳輸未經授權的命令。
- 資料加密:使用HTTPS對傳輸中的資料進行加密,保護敏感資訊
- 限制上傳大小:設定上傳檔案的大小限制,以防止拒絕服務攻擊
OWASP 十大漏洞提供了有關各種漏洞的詳細資訊以及保護 Web 應用程式安全的推薦實務。
5. 結論
在本文中,我們了解了在 servlet 中存取和處理POST請求負載的各種方法,考慮了從簡單表單資料到複雜 JSON 或 XML 以及多部分檔案的各種格式。我們也討論了對於建立安全且高效的 Web 應用程式至關重要的最佳實務。
完整的程式碼範例可以在 GitHub 上找到。