Java:SpringBoot给Controller添加统一路由前缀
作者:mmseoamin日期:2023-12-25

网上的文章五花八门,不写SpringBoot的版本号,导致代码拿来主义不好使了。

本文采用的版本

SpringBoot 2.7.7
Java 1.8

目录

    • 1、默认访问路径
    • 2、整个项目增加路由前缀
    • 3、通过注解方式增加路由前缀
    • 4、按照目录结构/包名添加前缀
    • 总结
    • 参考文章

      1、默认访问路径

      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

      2、整个项目增加路由前缀

      application.yml

      server:
        servlet:
          context-path: /prefix
      

      访问地址:http://localhost:8080/prefix/api/index

      注意:该方案会将所有的路由都增加一个前缀

      3、通过注解方式增加路由前缀

      注解

      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

      4、按照目录结构/包名添加前缀

      没有成功,可能是版本的问题

      按照网上的实现方式

      // 核心代码
      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包名规范的才可以,比如中划线就不行,需要修改代码自定义做映射

      参考文章

      • SpringBoot2.x 给Controller的RequestMapping添加统一前缀
      • SpringBoot - 根据目录结构自动生成路由前缀