芋道框架 - API 前缀区分机制

问题背景

在芋道框架中,门户端和管理后台的 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 包下但不包含 adminapp 子包即可(不推荐)。

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.**'
相关推荐
黑马师兄13 分钟前
RAG混合检索深度解析:让AI真正找到你要的内容
java·人工智能·ai·agent·rag·ai-native
码客日记17 分钟前
Spring Boot 配置文件敏感信息加密(Jasypt 企业级完整方案)
java·spring boot·git
IT知识分享37 分钟前
从零开发在线简繁转换工具:OpenCC 实战、避坑经验与方案选型
javascript·python
lunzi_082642 分钟前
【学习笔记】《Python编程 从入门到实践》第8章:函数定义、参数传递与模块导入
笔记·python·学习
凡人叶枫1 小时前
Effective C++ 条款04:确定对象被使用前已先被初始化
java·linux·开发语言·c++·嵌入式开发
杨运交1 小时前
[030][Web模块]Spring Boot 验证与 OpenAPI 集成实战:从校验规则到文档生成
前端·spring boot·python
云栖梦泽1 小时前
玩转RK3506SDK
linux·嵌入式硬件
极客先躯1 小时前
高级java每日一道面试题-2026年02月01日-实战篇[Docker]-Docker Volume 的生命周期管理是怎样的?
java·运维·docker·容器·持久化·架构图·容器卷
NE_STOP1 小时前
Raft算法处理细节
java
培培说证1 小时前
2026财务岗位如何快速提升自身能力
python