问题背景
在芋道框架中,门户端和管理后台的 Controller 可能有相同的 @RequestMapping,例如都是 /zhiping/news/page,但实际访问路径却不同:
- 管理后台:
/admin-api/zhiping/news/page - 门户端:
/app-api/zhiping/news/page
这是如何实现的?
核心机制
芋道框架通过 Controller 所在包路径 自动为接口添加不同的 URL 前缀。
1. 配置类 WebProperties.java
java
@ConfigurationProperties(prefix = "yudao.web")
@Data
public class WebProperties {
// 门户端 API 配置
private Api appApi = new Api("/app-api", "**.controller.app.**");
// 管理后台 API 配置
private Api adminApi = new Api("/admin-api", "**.controller.admin.**");
@Data
public static class Api {
/** URL 前缀 */
private String prefix;
/** Controller 所在包的 Ant 路径规则 */
private String controller;
}
}
2. 自动配置类 YudaoWebAutoConfiguration.java
java
@Bean
public WebMvcRegistrations webMvcRegistrations(WebProperties webProperties) {
return new WebMvcRegistrations() {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
// 核心:设置路径前缀映射
mapping.setPathPrefixes(buildPathPrefixes(webProperties));
return mapping;
}
private Map<String, Predicate<Class<?>>> buildPathPrefixes(WebProperties webProperties) {
Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
// 根据包路径匹配,自动添加前缀
putPathPrefix(pathPrefixes, webProperties.getAdminApi());
putPathPrefix(pathPrefixes, webProperties.getAppApi());
return pathPrefixes;
}
private void putPathPrefix(Map<String, Predicate<Class<?>>> pathPrefixes,
WebProperties.Api api, AntPathMatcher matcher) {
pathPrefixes.put(api.getPrefix(), // 前缀:/admin-api 或 /app-api
clazz -> clazz.isAnnotationPresent(RestController.class)
&& matcher.match(api.getController(), clazz.getPackage().getName()));
// 匹配:**.controller.admin.** 或 **.controller.app.**
}
};
}
目录结构规范
yudao-module-zhiping/src/main/java/cn/iocoder/yudao/module/zhiping/controller/
├── admin/ # 管理后台 Controller
│ └── news/
│ └── NewsController.java # → /admin-api/zhiping/news/**
└── app/ # 门户端 Controller
└── news/
└── AppNewsController.java # → /app-api/zhiping/news/**
示例对比
管理后台 Controller
java
// 包路径:cn.iocoder.yudao.module.zhiping.controller.admin.news
@RestController
@RequestMapping("/zhiping/news")
public class NewsController {
// 实际 URL:/admin-api/zhiping/news/page
@GetMapping("/page")
public CommonResult<PageResult<NewsRespVO>> getPage(...) { }
}
门户端 Controller
java
// 包路径:cn.iocoder.yudao.module.zhiping.controller.app.news
@RestController
@RequestMapping("/zhiping/news")
public class AppNewsController {
// 实际 URL:/app-api/zhiping/news/page
@GetMapping("/page")
public CommonResult<PageResult<AppNewsRespVO>> getPage(...) { }
}
为什么要这样设计?
| 优点 | 说明 |
|---|---|
| 安全性 | Nginx 只需转发 /admin-api/* 或 /app-api/*,避免内部接口泄露 |
| 权限隔离 | /admin-api 需要登录且有一定权限,/app-api 可以配置为 @PermitAll 允许匿名访问 |
| 代码复用 | 同一个模块可以同时提供管理端和门户端接口,复用 Service 层代码 |
| Swagger 分组 | 可按前缀分组展示 API 文档 |
常见问题
Q1: 为什么我的接口没有前缀?
A: 检查 Controller 所在包路径是否正确:
- 管理后台:必须包含
controller.admin - 门户端:必须包含
controller.app
Q2: 如何配置不需要前缀的接口?
A: 放在 controller 包下但不包含 admin 或 app 子包即可(不推荐)。
Q3: 前端请求应该用什么路径?
A:
- 管理后台前端:
/admin-api/模块/功能/操作 - 门户前端:
/app-api/模块/功能/操作
扩展:自定义前缀
如需修改默认前缀,在 application.yaml 中配置:
yaml
yudao:
web:
admin-api:
prefix: /admin-api
controller: '**.controller.admin.**'
app-api:
prefix: /app-api
controller: '**.controller.app.**'