1. 引言
在 Spring Boot 應用中,每個控制器都可以擁有自己的 URL 映射。這使得單個應用能夠輕鬆地在多個位置提供 Web 端點。例如,我們可以將我們的 API 端點分組到邏輯分組中,如內部和外部。
然而,在某些情況下,我們可能希望所有端點都位於一個通用的前綴下。在本教程中,我們將探討使用通用前綴為所有 Spring Boot 控制器提供不同方法。
2. Servlet 上下文
Spring 應用中負責處理 Web 請求的主要組件是 DispatcherServlet。 通過自定義該組件,我們對請求的路由擁有相當程度的控制權。
讓我們來看兩種自定義 DispatcherServlet 的方法,以便使我們應用程序的所有端點都可用在同一個 URL 前綴下。
2.1. Spring Bean
通過引入一個新的 Spring Bean 來實現:
@Configuration
public class DispatcherServletCustomConfiguration {
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/api/");
registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registration;
}
}在這裏,我們正在創建一個 ServletRegistrationBean,該類將 DispatcherServlet bean 包裝起來。請注意,我們提供了明確的基 URL 為 /api/。 這意味着所有我們的端點都必須通過該基 URL 前綴訪問
2.2. 應用屬性
我們可以僅通過使用應用屬性來達到相同的效果。在 Spring Boot 2.0.0 及更高版本中,我們只需將以下內容添加到我們的 <em >application.properties</em > 文件中:
server.servlet.contextPath=/api在此版本之前,屬性名稱略有不同:
server.contextPath=/api這種方法的一個優勢在於它僅使用標準的 Spring 屬性。 這意味着我們可以輕鬆地使用標準機制(如 profile 或外部屬性綁定)來更改或覆蓋我們的通用前綴。
2.3. 優缺點
這些兩種方法的的主要優勢也是其主要缺點:它們會影響應用程序中的每一個端點。
對於某些應用程序而言,這可能完全可以接受。然而,某些應用程序可能需要使用標準端點映射來與第三方服務進行交互——例如,OAuth 交換。在這種情況下,像這樣全局的解決方案可能並不適用。
3. 註解
另一個添加前綴到 Spring 應用中所有控制器的辦法是使用註解。下面我們將探討兩種不同的方法。
3.1. SpEL
使用 Spring 表達式語言 (SpEL) 與標準 <em/>@RequestMapping 註解相結合的第一種方法。通過這種方法,我們只需為每個想要添加前綴的控制器添加一個屬性:
@Controller
@RequestMapping(path = "${apiPrefix}/users")
public class UserController {
} 然後,我們只需在 application.properties 文件中指定屬性值:
apiPrefix=/api3.2. 自定義標註
另一種實現此目的的方法是創建我們自己的標註:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@RequestMapping("/api/")
public @interface ApiPrefixController {
@AliasFor(annotation = Component.class)
String value() default "";
}然後,我們只需要將標註應用於我們想要添加前綴的每個控制器:
@Controller
@ApiPrefixController
public class SomeController {
@RequestMapping("/users")
@ReponseBody
public String getAll(){
// ...
}
}3.3. 優缺點
這兩種方法解決了前一種方法的主要問題:它們都提供了對哪些控制器獲得前綴的精細控制。我們可以僅對特定控制器應用註解,而不是影響應用程序中的所有端點。
4. 服務器端重定向
最後一種方法是使用服務器端重定向。 與重定向不同,重定向不涉及向客户端發送響應。這意味着我們的應用程序可以在不影響客户端的情況下,在端點之間傳遞請求。
為了開始,讓我們編寫一個簡單的控制器,其中包含兩個端點:
@Controller
class EndpointController {
@GetMapping("/endpoint1")
@ResponseBody
public String endpoint1() {
return "Hello from endpoint 1";
}
@GetMapping("/endpoint2")
@ResponseBody
public String endpoint2() {
return "Hello from endpoint 2";
}
}接下來,我們創建一個基於我們想要的前綴的新控制器:
@Controller
@RequestMapping("/api/endpoint")
public class ApiPrefixController {
@GetMapping
public ModelAndView route(ModelMap model) {
if(new Random().nextBoolean()) {
return new ModelAndView("forward:/endpoint1", model);
}
else {
return new ModelAndView("forward:/endpoint2", model);
}
}
}此控制器具有一個單一的端點,它充當路由器。在這種情況下,它基本上通過隨機選擇來將原始請求轉發到我們其他兩個端點之一。
我們可以通過發送幾個連續請求來驗證其功能:
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2
> curl http://localhost:8080/api/endpoint
Hello from endpoint 1
> curl http://localhost:8080/api/endpoint
Hello from endpoint 1
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2這種方法的關鍵優勢在於其強大性。我們可以應用任何邏輯來確定如何處理請求:URL路徑、HTTP方法、HTTP頭等。
5. 結論
在本文中,我們學習瞭如何將常用前綴應用於 Spring 應用程序中的每個控制器。 就像大多數決策一樣,每種方法都有其優缺點,在實施之前應仔細考慮。