{"record":{"id":"37ed702ed7eee986","repo":"yudaocode/SpringBoot-Labs","slug":"1001002000-37ed70","errorCode":"1001002000","errorMessage":"用户不存在","messagePattern":"用户不存在","errorType":"http","errorClass":"ServiceException","httpStatus":500,"severity":"error","filePath":"lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java","lineNumber":109,"sourceCode":"        UserVO user = new UserVO().setId(id).setUsername(\"username:\" + id);\n        // 返回\n        return CommonResult.success(user);\n    }\n\n    /**\n     * 测试抛出 NullPointerException 异常\n     */\n    @GetMapping(\"/exception-01\")\n    public UserVO exception01() {\n        throw new NullPointerException(\"没有粗面鱼丸\");\n    }\n\n    /**\n     * 测试抛出 ServiceException 异常\n     */\n    @GetMapping(\"/exception-02\")\n    public UserVO exception02() {\n        throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND);\n    }\n\n//    @PostMapping(value = \"/add\",\n//            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Content-Type 请求头\n//            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},\n//            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Accept 请求头\n//            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}\n//    )\n\n    @PostMapping(value = \"/add\",\n            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Content-Type 请求头\n            consumes = {MediaType.APPLICATION_XML_VALUE},\n            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Accept 请求头\n            produces = {MediaType.APPLICATION_XML_VALUE}\n    )\n//    @PostMapping(value = \"/add\")\n    public Mono<UserVO> add(@RequestBody Mono<UserVO> user) {\n        return user;","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/yudaocode/SpringBoot-Labs/blob/6c12efaed06d12907a0f40dd2ad1f7020aec8798/lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java#L91-L127","documentation":"ServiceException(USER_NOT_FOUND, code 1001002000, '用户不存在') thrown from GET /user/exception-02 in the WebFlux lab. It demonstrates that @ControllerAdvice + @ExceptionHandler works identically in Spring WebFlux for exceptions thrown synchronously from annotated controllers, converting the business exception into a CommonResult payload with code 1001002000.","triggerScenarios":"Calling GET /user/exception-02 on the WebFlux app; the method throws unconditionally. Note the surrounding demo also has commented-out /add mappings showing consumes/produces negotiation — only the exception path matters here.","commonSituations":"Verifying the unified error contract ({code, message, data}) in a reactive service before writing gateway/client code. Developers also hit this shape when a reactive service layer throws ServiceException from inside flatMap: the handler still catches it, but only if the error actually propagates out of the chain (beware swallowed errors in doOnNext without error consumers).","solutions":["Expect the CommonResult contract (code 1001002000) in clients; do not parse HTTP status alone.","Confirm your @ControllerAdvice class is component-scanned by the WebFlux app (same base package as the Application class).","When throwing ServiceException from reactive operators, make sure it is returned as Mono.error/Flux.error so the dispatcher sees it and the advice fires.","Keep ServiceExceptionEnum in a shared module so MVC and WebFlux services expose the same codes."],"exampleFix":"// before: business exception thrown inside a reactive lambda may be swallowed\nuserRepository.findById(id)\n    .doOnNext(u -> { if (u == null) throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND); })\n    .map(this::toVO);\n\n// after: propagate through the error channel\nuserRepository.findById(id)\n    .switchIfEmpty(Mono.error(new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND)))\n    .map(this::toVO);","handlingStrategy":"try-catch","validationCode":"// For real lookups, switch on empty before the exception can be thrown:\nreturn userRepository.findById(id)\n    .switchIfEmpty(Mono.error(new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND)));","typeGuard":null,"tryCatchPattern":"// Server-side unified handling:\n@ExceptionHandler(ServiceException.class)\nCommonResult<?> serviceEx(ServiceException ex) {\n    return CommonResult.error(ex.getCode(), ex.getMessage());\n}","preventionTips":["Always return errors via Mono.error/Flux.error in reactive chains so handlers see them.","Share ServiceException(Enum) via a common module across MVC and WebFlux services.","Test the error path with WebTestChain (WebTestClient) to lock the CommonResult shape."],"tags":["webflux","reactive","business-exception","error-code","tutorial"],"backgroundTag":null,"analyzedSha":"6c12efaed06d12907a0f40dd2ad1f7020aec8798","analyzedAt":"2026-08-14T13:06:31.500Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}