简介
项目uri设计时,正常都会有个公共路径,如”/api/login”、”/api/logout”,这里”/api”就是公共路径。为了避免在每一个controller里添加这个api,可以使用一些技术方案来一次性处理一下。
方案
方案一:自定义DispatcherServlet
Spring 应用程序中负责处理 Web 请求的主要组件是DispatcherServlet
。通过自定义此组件,我们可以对请求的路由方式进行相当程度的控制。让我们看一下自定义DispatcherServlet
的两种不同方法,这将使我们的所有应用程序端点都可以通过公共 URL 前缀使用。
a: 使用configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // 创建package.config.DispatcherServletCustomConfig
@Configuration
public class DispatcherServletCustomConfig {
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(dispatcherServlet(), "/api/*");
registrationBean.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registrationBean;
}
}
|
1
2
3
4
5
6
7
8
9
| // 在package.controller.HelloController里
// 这样就可以通过/api/hello访问此controller method了
@RestController
public class HelloController {
@GetMapping("/hello")
public ResponseEntity<Object> hello() {
return new ResponseEntity<>("hello", HttpStatus.OK);
}
}
|
b: 直接使用springboot的配置文件
1
| server.servlet.contextPath = /api
|
方案的优缺点
虽然可以简单的一次性全部添加/api
路径很方便,但是对于一些不想使用/api
做公共路径的服务,处理起来就很麻烦了。
方案二:使用注解
a. 直接使用Spring扩展语言
1
2
| # 首先直接在springboot配置文件application.properties添加一下内容
apiPrefix = /api
|
1
2
3
4
5
6
7
8
| // 然后注解时添加变量
@RestController
public class HelloController {
@GetMapping("${apiPrefix}/hello")
public ResponseEntity<Object> hello() {
return new ResponseEntity<>("hello", HttpStatus.OK);
}
}
|
a. 自定义注解
1
2
3
4
5
6
7
8
9
10
| // 创建package.annotation.ApiPrefixController
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@RequestMapping("/api")
public @interface ApiPrefixController {
@AliasFor(annotation = Component.class)
String value() default "";
}
|
1
2
3
4
5
6
7
8
9
| // 在controller使用自定义注解
@ApiPrefixController
@RestController
public class HelloController {
@GetMapping("/hello")
public ResponseEntity<Object> hello() {
return new ResponseEntity<>("hello", HttpStatus.OK);
}
}
|
方案的优缺点
相比前面的方案,这个方案定制化程度很高,而且可以细粒度到每个方法上。
方案三:使用服务端路由转发
a. 路由转发
这个转发并不是301/302给客户端,而是直接根据逻辑内部分流到子服务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 创建目标控制器,有多个方法,定义多个http入口
@Controller
class DemoEndpointController {
@GetMapping("/endpoint1")
@ResponseBody
public String endpoint1() {
return "endpoint1";
}
@GetMapping("/endpoint2")
@ResponseBody
public String endpoint2() {
return "endpoint2";
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 创建路由类,这里我根据条件跳转到/endpoint1或/endpoint2
@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);
}
}
}
|
方案的优缺点
这个方案和前面几种方案不同,它使用/api/endpoint
作为uri,这样做的好处就是可以根据http的方法、header的参数等进行路由切换,缺点是我们经常需要/api/login
、/api/logout
在uri上区分好业务功能。