芋道框架 - 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 包下但不包含 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.**'
相关推荐
一条小小yu14 小时前
为什么使用springboot
java·spring boot·后端
Elastic 中国社区官方博客14 小时前
Kubernetes attributes processor v1:它对 EDOT Collector 意味着什么
java·大数据·elasticsearch·搜索引擎·贪心算法·kubernetes·全文检索
happylifetree14 小时前
Python15:核心语法-数据存储与运算-字符串拼接
python
Sarvartha14 小时前
对象创建与引用
java·开发语言
周洲083014 小时前
STM32片内Flash读写深度详解|掉电参数保存、底层原理
linux·前端·stm32
shimly12345614 小时前
比 find + grep 更好用的 nm + addr2line
linux
迷途之人不知返15 小时前
【进程控制】-3-进程等待
linux
大侠归来15 小时前
C语言与C++在基础语法上的区别
java·c语言·c++
huisheng_qaq15 小时前
【Python基础篇-09】深入理解python的闭包与装饰器
python·闭包
_upupup15 小时前
包管理器和vim
linux·服务器