使用 Java 從 HTTP 或 HTTPS 請求取得帶有連接埠的主機名
1.概述
在建立 Web 應用程式時,我們經常需要從傳入的 HTTP 或 HTTPS 請求中提取方案、主機名稱和連接埠。例如,我們可能需要它產生絕對連結、建立重定向 URL,或記錄詳細的請求資訊以供偵錯。
在本教程中,我們將探索在 Java 中從請求中取得主機名稱和連接埠的不同方法。我們將從標準的HttpServletRequest方法開始,然後介紹 Spring 的輔助實用程序,並討論如何處理代理等實際場景。
2.使用HttpServletRequest API
最直接的方法是使用HttpServletRequest ,它直接提供方案、主機和連接埠:
public static String getHostWithPort(HttpServletRequest request) {
String scheme = request.getScheme();
String serverName = request.getServerName();
int serverPort = request.getServerPort();
boolean isDefaultPort = ("http".equals(scheme) && serverPort == 80) || ("https".equals(scheme) && serverPort == 443);
if (isDefaultPort) {
return String.format("%s://%s", scheme, serverName);
} else {
return String.format("%s://%s:%d", scheme, serverName, serverPort);
}
}
這裡,我們檢查連接埠是否是預設連接埠(80 或 443),如果是,請忽略它。這樣可以保持 URL 的簡潔性和一致性。
3. 使用HttpServletRequest.getRequestURL()
如果我們想要整個 URL,包括路徑和查詢參數,我們可以使用getRequestURL():
public static String getHostWithPortFromRequestUrl(HttpServletRequest request) {
try {
URL url = new URL(request.getRequestURL().toString());
return url.getPort() == -1
? String.format("%s://%s", url.getProtocol(), url.getHost())
: String.format("%s://%s:%d", url.getProtocol(), url.getHost(), url.getPort());
} catch (MalformedURLException e) {
throw new RuntimeException("Invalid request URL", e);
}
}
該方法很簡單,並且適用於 HTTP 和 HTTPS,但由於涉及解析 URL 字串,因此效率稍低。
4.使用 Spring 的ServletUriComponentsBuilder
如果我們使用 Spring Boot 或 Spring MVC, ServletUriComponentsBuilder提供了一個簡潔的 API 來根據目前請求建立 URL:
public static String getBaseUrl() {
return ServletUriComponentsBuilder.fromCurrentRequestUri()
.replacePath(null)
.build()
.toUriString();
}
這樣,我們就可以自動處理連接埠和 HTTPS。這是一種簡潔的聲明式語法。但它僅適用於 Spring Web 上下文。
5.處理反向代理( X-Forwarded-*標頭)
在生產環境中,我們的應用通常位於反向代理之後,例如 Nginx、AWS ELB 或 Cloudflare。這些代理程式可能會在轉送請求之前修改主機和連接埠。
為了正確處理這個問題,我們可以使用轉送標頭:
public static String getForwardedHost(HttpServletRequest request) {
String forwardedHost = request.getHeader("X-Forwarded-Host");
String forwardedProto = request.getHeader("X-Forwarded-Proto");
String forwardedPort = request.getHeader("X-Forwarded-Port");
String scheme = forwardedProto != null ? forwardedProto : request.getScheme();
String host = forwardedHost != null ? forwardedHost : request.getServerName();
String port = forwardedPort != null ? forwardedPort : String.valueOf(request.getServerPort());
boolean isDefaultPort = ("http".equals(scheme) && "80".equals(port))
|| ("https".equals(scheme) && "443".equals(port));
return isDefaultPort ? String.format("%s://%s", scheme, host) : String.format("%s://%s:%s", scheme, host, port);
}
這種取得主機和連接埠的方式反映了真實的客戶端 URL。它與負載平衡器和 CDN 配合良好。但這取決於代理配置。
對於 Spring Boot 應用程序,啟用ForwardedHeaderFilter可以自動完成此操作。
6. 結論
在本文中,我們探討了幾種使用 Java 從 HTTP 或 HTTPS 請求中提取方案、主機名稱和連接埠的方法。每種方法都服務於特定的用例; HttpServletRequest API 提供了一個簡單、可移植的解決方案。當我們需要完整的 URL 時, getRequestURL()非常有效; ServletUriComponentsBuilder是 Spring MVC 或 Spring Boot 應用程式的理想選擇;而當我們的應用程式在負載平衡器或反向代理之後運行時,像X-Forwarded-*這樣的代理標頭是必不可少的。
在大多數情況下,基於 servlet 或基於 Spring 的方法就足夠了,而代理支援在生產環境中至關重要。與往常一樣,本文中提供的程式碼可在 GitHub 上取得。