网上的文章五花八门,不写SpringBoot的版本号,导致代码拿来主义不好使了。
本文采用的版本
SpringBoot 2.7.7 Java 1.8
package com.example.demo.controller.api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class AppIndexController { @GetMapping("/index") public String index() { return "app"; } }
访问地址:http://localhost:8080/api/index
application.yml
server: servlet: context-path: /prefix
访问地址:http://localhost:8080/prefix/api/index
package com.example.demo.annotation; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RestController; import java.lang.annotation.*; /** * controller层统一使用该注解 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @RestController public @interface ApiRestController { /** * Alias for {@link Controller#value}. */ @AliasFor(annotation = Controller.class) String value() default ""; }
配置
package com.example.demo.config; import com.example.demo.annotation.ApiRestController; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 配置统一的后台接口访问路径的前缀 */ @Configuration public class CustomWebMvcConfig implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer .addPathPrefix("/api", c -> c.isAnnotationPresent(ApiRestController.class)); } }
使用注解
package com.example.demo.controller.api; import com.example.demo.annotation.ApiRestController; import org.springframework.web.bind.annotation.GetMapping; @ApiRestController // @RestController // @RequestMapping("/api") public class AppIndexController { @GetMapping("/index") public String index() { return "app"; } }
访问地址:http://localhost:8080/api/index
没有成功,可能是版本的问题
按照网上的实现方式
// 核心代码 RequestMappingInfo.paths(prefix).build().combine(mappingInfo);
会报错
Neither PathPatterns nor String patterns condition
2023年6月9日补充
感谢评论区的大佬 @孤独和弦 帮助,补充第四种方式
思路:
将原有路由的所有路径取出,手动拼接前缀,再和原有路由配置合并
项目结构
$ tree -I target -I test . ├── pom.xml └── src └── main ├── java │ └── com │ └── example │ └── demo │ ├── Application.java │ ├── config │ │ ├── AutoPrefixConfiguration.java │ │ └── AutoPrefixUrlMapping.java │ └── controller │ └── v1 │ └── IndexController.java └── resources ├── application.yml ├── static └── templates
配置 application.yml
# 需要添加路径前缀的包名 api-package: com.example.demo.controller
AutoPrefixUrlMapping.java
package com.example.demo.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Method; import java.util.Objects; /** * 自动补全路由前缀处理类 */ public class AutoPrefixUrlMapping extends RequestMappingHandlerMapping { /** * 读取基础包配置 */ @Value("${api-package}") private String bathApiPackagePath; /** * 重写方法路由获取 * * @param method * @param handlerType * @return */ @Override protected RequestMappingInfo getMappingForMethod(Method method, Class> handlerType) { RequestMappingInfo mappingInfo = super.getMappingForMethod(method, handlerType); if (Objects.nonNull(mappingInfo)) { String prefix = this.getPrefix(handlerType); if (prefix != null) { String[] paths = mappingInfo.getPatternValues() .stream() .map(path -> prefix + path) .toArray(String[]::new); return mappingInfo.mutate() .paths(paths) .build(); } } return mappingInfo; } /** * 获取方法路由前缀 * * @param handleType * @return */ private String getPrefix(Class> handleType) { String packageName = handleType.getPackage().getName(); // 如果包含指定的包则返回前缀 if (packageName.startsWith(this.bathApiPackagePath)) { return packageName.substring(this.bathApiPackagePath.length()) .replace(".", "/"); } else { return null; } } }
AutoPrefixConfiguration.java
package com.example.demo.config; import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.stereotype.Component; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; /** * 自动补全路由前缀配置类 */ @Component public class AutoPrefixConfiguration implements WebMvcRegistrations { @Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return new AutoPrefixUrlMapping(); } }
控制器
package com.example.demo.controller.v1; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class IndexController { @GetMapping("/index") public String index() { return "Hello"; } }
访问路径:http://localhost:8080/v1/api/index
方 式 | 适用范围 |
---|---|
RequestMapping/PostMapping/GetMapping | 单个方法 或 单个类(多个方法) |
自定义注解 | 多个控制器(可以不同目录) |
目录 / 包名 前缀 | 多个控制器(同目录) |
配置 context-path | 全局前缀 |
包名前缀只能是符合java包名规范的才可以,比如中划线就不行,需要修改代码自定义做映射