Spring - 通过异常处理程序拦截来自另一个 bean 的响应

Spring - Intercept response from another bean by exception handler

我有两个 @RestController-(A 和 B)并注册了 ResponseEntityExceptionHandler。是否有可能(以及如何做)从 A 调用并在应用异常处理程序后从 B 获得响应?

示例:

  1. 用户休息电话A
  2. AgetPerson
  3. 调用 B
  4. B 抛出异常 NotFound
  5. NotFound 由异常处理程序处理,转换 ResponseEntity 并放入 400 status
  6. B 终于 return 异常 ResponseEntity
  7. AB
  8. 获得 400 状态
  9. A 可以获得这个 400 并用它做点什么

简单 @Autowired 不起作用。

片段:

答:

@RestController
@RequestMapping("/v1")
public class A {

    private final B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }

    @PostMapping(
        value = "persons",
        consumes = "application/json",
        produces = "application/json")
    public ResponseEntity<List<StatusResponse<Person>>> addPersons(final List<Person> persons) {
        final List<StatusResponse<Person>> multiResponse = new ArrayList<>();
        for(final Person p: persons) {
            final ResponseEntity<Person> response = b.addPerson(person);
            multiResponse.add(new StatusResponse<>(
                response.getStatusCode(), response.getMessage(), response.getBody()
            ));
        }
        return ResponseEntity.status(HttpStatus.MULTI_STATUS).body(multiResponse);
    }

}

乙:

@RestController
@RequestMapping("/v1")
public class B {

    @PostMapping(
        value = "person",
        consumes = "application/json",
        produces = "application/json")
    public ResponseEntity<Person> addPerson(final Person person) {
        accessService.checkAccess();
        return ResponseEntity.status(201).body(
            logicService.addPerson(person)
        );
    }

}

处理程序

@ControllerAdvice
public final class MyExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MyException.class)
    protected ResponseEntity<Object> handleApiException(final MyException exception, final WebRequest webRequest) {
        //logic
        return afterLogic;
    }

}

forward 类似于重定向,但完全发生在服务器端; Servlet 容器将相同的请求转发到目标 URL; URL 在浏览器中不会改变。 在 spring 你可以这样做,你可以传递属性 :

@Controller
@RequestMapping("/")
public class YourController {

    @GetMapping("/forward")
    public ModelAndView redirectWithUsingForwardPrefix(ModelMap model) {
        model.addAttribute("attribute", "forward");
        return new ModelAndView("forward:/redirectedUrl", model);
    }
}

无法将控制权从处理完其方法后调用的异常处理程序返回给控制器。您当前的流程如下所示call A.addPersons -> invoke B.addPerson -> B throws exception -> exception is propagate to A controller 并在处理控制器方法(不是状态为 400 的 ResponseEntity)后保存为 dispatchException 以在 DispatcherServlet 中进一步处理 -> 使用 MyExceptionHandler 处理异常。从这个地方你不能回到控制器。

我不确定你想用控制器中的这个异常做什么,但解决方案可能如下所示:

@RestController
@RequestMapping("/resources")
public class AController {

    private BService service;

    @Autowired
    public AController(BService service) {
        this.service = service;
    }

    @RequestMapping("/test")
    public ResponseEntity<String> test() {
        ResponseEntity<String> result = service.test();
        if (result.hasBody()) {
            //doSomething
        }

        return result; //or list like you did
    }
}

@Service
public class BService {

    public ResponseEntity<String> test() {
        try {
            return ResponseEntity.status(201).body(getResponse()); //this always throws exception. It's just for purpose of example
        } catch (CustomException ex) {
            return ResponseEntity.status(400).build();
        }

    }

    private String getResponse() {
        throw new CustomException("Not OK!");
    }
}