1. 前言
在微服务项目中,建议所有接口使用统一的API前缀,多类业务可用不同前缀区分。这样便于Nginx/Openresty等反向代理统一处理,好处如下:
简化配置:统一前缀可减少API代理配置项,降低管理复杂度和出错风险;
便于开发:统一前缀利于为相同业务编写Lua插件,实现诸如鉴权等功能;
保障安全:避免Swagger、Actuator等敏感接口暴露到公网,降低安全风险。
2. 解决方案
在项目开发中,通常采用Maven构建多模块架构,将项目拆分为多个独立的模块,每个模块专注于特定的功能或业务逻辑。在此基础上,我们可以将API前缀的统一配置项集中放置在基础模块中,供各个业务模块进行引用。
接下来,将以yudao-cloud项目为例进行详细说明。
2.1 配置统一的controller包名
在统一API前缀之前,必须确保模块中的Controller包名遵循统一的命名规则。在yudao-cloud项目中,与Admin相关的API所在的Controller包名采用“**.controller.admin.**”的形式,而业务相关的API所在的Controller包名则采用“**.controller.app.**”的形式,具体如下所示:
@ConfigurationProperties(prefix = "yudao.web")
@Validated
@Data
public class WebProperties {
@NotNull(message = "APP API 不能为空")
private Api appApi = new Api("/app-api", "**.controller.app.**");
@NotNull(message = "Admin API 不能为空")
private Api adminApi = new Api("/admin-api", "**.controller.admin.**");
@Data
@AllArgsConstructor
@NoArgsConstructor
@Valid
public static class Api {
/**
* API 前缀,实现所有 Controller 提供的 RESTFul API 的统一前缀
*
*
* 意义:通过该前缀,避免 Swagger、Actuator 意外通过 Nginx 暴露出来给外部,带来安全性问题
* 这样,Nginx 只需要配置转发到 /api/* 的所有接口即可。
*
* @see YudaoWebAutoConfiguration#configurePathMatch(PathMatchConfigurer)
*/
@NotEmpty(message = "API 前缀不能为空")
private String prefix;
/**
* Controller 所在包的 Ant 路径规则
*
* 主要目的是,给该 Controller 设置指定的 {@link #prefix}
*/
@NotEmpty(message = "Controller 所在包不能为空")
private String controller;
}
}
2.2 添加统一API前缀
通过实现 WebMvcConfigurer 接口并重写 configurePathMatch 方法,可以利用 AntPathMatcher 进行路径匹配,从而为符合特定要求的请求路径添加统一的 API 前缀。
@AutoConfiguration
@EnableConfigurationProperties(WebProperties.class)
public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
@Resource
private WebProperties webProperties;
/**
* 应用名
*/
@Value("${spring.application.name}")
private String applicationName;
@Override
public void configurePathMatch(@NotNull PathMatchConfigurer configurer) {
configurePathMatch(configurer, webProperties.getAdminApi());
configurePathMatch(configurer, webProperties.getAppApi());
}
/**
* 设置 API 前缀,仅仅匹配 controller 包下的
*
* @param configurer 配置
* @param api API 配置
*/
private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
AntPathMatcher antPathMatcher = new AntPathMatcher(".");
configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class)
&& antPathMatcher.match(api.getController(), clazz.getPackage().getName())); // 仅仅匹配 controller 包
}
}