You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot路径继承与统一路径变量校验方案咨询

解决方案

针对你在Spring Boot 3中遇到的统一校验{project-name}路径变量并注入项目实体的需求,以下是几种符合MVC/Webflux层面的实现方案:

方案一:@ControllerAdvice + @ModelAttribute(推荐MVC使用)

通过全局控制器通知,为指定范围的Controller统一处理project-name的校验与实体注入,无需在每个Controller中重复编写逻辑。

步骤1:定义范围标记(可选)

创建自定义注解,用来标记需要应用该逻辑的Controller:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ProjectScoped {
}

步骤2:编写全局处理类

创建全局控制器通知类,拦截所有标记了@ProjectScoped的Controller,处理路径变量并注入项目实体:

@ControllerAdvice(annotations = ProjectScoped.class)
public class ProjectGlobalAdvice {

    @Autowired
    private ProjectService projectService;

    @ModelAttribute("project")
    public Project resolveProject(@PathVariable("project-name") String projectName) {
        // 校验项目名称合法性,查询项目实体
        Project project = projectService.getValidProject(projectName);
        if (project == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "项目不存在");
        }
        return project;
    }
}

步骤3:在业务Controller中使用

每个业务Controller(a、b、c、d)只需标记@ProjectScoped,并在方法中直接注入项目实体:

@RestController
@ProjectScoped
@RequestMapping("/projects/{project-name}/a")
public class AController {

    @GetMapping("/details")
    public ResponseEntity<AEntity> getADetails(@ModelAttribute("project") Project project) {
        // 直接使用已校验的project实体处理业务
        AEntity entity = getADataByProject(project);
        return ResponseEntity.ok(entity);
    }
}

方案二:RequestMappingHandlerInterceptor(MVC进阶方案)

通过Spring MVC的拦截器,在请求进入Controller前统一处理路径变量,将校验后的项目实体存入请求属性,供后续Controller使用。

步骤1:实现拦截器

@Component
public class ProjectInterceptor implements HandlerInterceptor {

    @Autowired
    private ProjectService projectService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从路径中提取project-name参数
        String projectName = (String) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE).get("project-name");
        
        // 校验并查询项目实体
        Project project = projectService.getValidProject(projectName);
        if (project == null) {
            response.sendError(HttpStatus.NOT_FOUND.value(), "项目不存在");
            return false;
        }
        
        // 将实体存入请求属性,供Controller获取
        request.setAttribute("project", project);
        return true;
    }
}

步骤2:注册拦截器

通过WebMvcConfigurer配置拦截器的生效路径:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor)
                .addPathPatterns("/projects/**"); // 匹配所有项目相关路径
    }
}

步骤3:Controller中使用

在业务Controller方法中,通过@RequestAttribute直接获取已校验的项目实体:

@RestController
@RequestMapping("/projects/{project-name}/b")
public class BController {

    @GetMapping("/info")
    public ResponseEntity<BEntity> getBInfo(@RequestAttribute("project") Project project) {
        BEntity entity = getBDataByProject(project);
        return ResponseEntity.ok(entity);
    }
}

方案三:Webflux 适配方案

如果使用的是Spring Webflux,可采用以下两种方式:

方式1:@ControllerAdvice + @ModelAttribute

与MVC方案类似,全局通知类同样适用Webflux:

@ControllerAdvice(annotations = ProjectScoped.class)
public class ProjectGlobalAdvice {

    @Autowired
    private ProjectService projectService;

    @ModelAttribute("project")
    public Mono<Project> resolveProject(@PathVariable("project-name") String projectName) {
        return projectService.getValidProject(projectName)
                .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, "项目不存在")));
    }
}

业务Controller中直接注入:

@RestController
@ProjectScoped
@RequestMapping("/projects/{project-name}/c")
public class CController {

    @GetMapping("/data")
    public Mono<ResponseEntity<CEntity>> getCData(@ModelAttribute("project") Mono<Project> projectMono) {
        return projectMono.map(project -> {
            CEntity entity = getCDataByProject(project);
            return ResponseEntity.ok(entity);
        });
    }
}

方式2:HandlerFilterFunction(Webflux拦截器)

@Component
public class ProjectFilterFunction implements HandlerFilterFunction<ServerResponse, ServerResponse> {

    @Autowired
    private ProjectService projectService;

    @Override
    public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction<ServerResponse> next) {
        String projectName = request.pathVariable("project-name");
        return projectService.getValidProject(projectName)
                .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, "项目不存在")))
                .flatMap(project -> next.handle(request.attribute("project", project).build()));
    }
}

注册过滤器:

@Configuration
public class WebfluxConfig {

    @Autowired
    private ProjectFilterFunction projectFilterFunction;

    @Bean
    public RouterFunction<ServerResponse> routerFunction() {
        return RouterFunctions.route()
                .path("/projects", builder -> builder
                        .nest(path("/{project-name}"), nestedBuilder -> nestedBuilder
                                .add(projectFilterFunction)
                                // 可继续配置各业务路由,或在业务Router中引用过滤器
                        ))
                .build();
    }
}

内容的提问来源于stack exchange,提问作者NickDelta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 06:04:56