使用RestTemplate异常处理将端点弹到端点

时间:2017-10-16 15:53:42

标签: java spring rest exception

我们有一个微服务架构。每个服务通过Rest公开数据。所有控制器都使用Spring设置:

@RestController
@RequestMapping(path = "foobar")
public class UiController {

      @PostMapping("foo")
      public ResponseEntity<Foo> addFoo(@RequestBody final FooDto fooDto) {

           Foo fromDb = adminService.addFoo(converterToModel.convert(fooDto);
           return ResponseEntity.ok(converterToDto.convert(fromDb));

      }

如果由于某种原因fooDto无法添加到数据库中。抛出自定义异常:

@ResponseStatus(value = HttpStatus.CONFLICT)
public class FooAlreadyAssignedException extends RuntimeException {

    public FooAlreadyAssignedException(String msg) {
        super("The following foos are already assigned to foobar: " + msg);
    }

}

在Postman中,您会在抛出上述异常后看到以下JSON

{
    "timestamp": 1508247298817,
    "status": 409,
    "error": "Conflict",
    "exception": "com.foo.exception.FooCodeAlreadyExistsException",
    "message": "A foo with code: foo already exists",
    "path": "/foo/foobar"
}

我们有4种不同的服务,所有这些都以相同的方式设置。

我们的UI是在Angular 4中制作的,并对我们的网关进行REST调用。网关是微服务和UI之间的连接。它还公开了一个REST端点。它也是用Spring实现的。我添加了一张图片以便澄清:

architecture

“编辑:我看到我没有完成箭头。当然所有数据都会传回给用户界面”

问题

Gateway使用RestTemplate来调用微服务的API 当微服务中抛出自定义异常时,网关返回:

{
"timestamp": "2017-10-16T15:30:03.456Z",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.web.client.HttpClientErrorException",
"message": "409 null",
"path": "/v1/printstations"
}

我的原始响应是HttpStatus.conflict(status = 409)似乎被网关包含在状态500消息中。我不想要这种行为,我希望它将原始消息传递给UI。

有关如何控制此行为的任何想法?

备注

我已经和Postman一起测试过,如果你直接访问微服务,它会返回409,并在自定义异常中写入消息

我已经尝试覆盖Springs ResponseErrorHandler,但无法找到合适的解决方案。

2 个答案:

答案 0 :(得分:1)

在spring rest模板调用你的微服务的网关代码中,我建议捕获HttpClientErrorException,然后像下面的例子一样创建你自己的异常类,如ApiException,这样你就可以传递从中抛出的确切异常微服务:

catch (org.springframework.web.client.HttpClientErrorException e) {

            throw new ApiException(e.getMessage(), e, e.getRawStatusCode(), e.getResponseHeaders(),
                    e.getResponseBodyAsString(), fullURIPath, null);
        }

其中ApiException有一个如下构造函数:

public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders,
            String responseBody, String requestURI, String requestBody) {
        super(message, throwable);
        this.code = code;
        this.responseHeaders = responseHeaders;
        this.responseBody = responseBody;
        this.requestURI = requestURI;
        this.requestBody = requestBody;
    }

答案 1 :(得分:1)

可以关闭问题。

解决方案是将微服务中发生的异常映射到网关中的有效ResponseEntity,以便网关中的RestTemplate不会在500服务器错误中重新打包错误。

我们通过创建@ControllerAdvice类

来完成此操作
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler(value = {HttpClientErrorException.class})
protected ResponseEntity<Object> handleConflict(HttpClientErrorException ex, WebRequest request) {

    return handleExceptionInternal(ex, ex.getResponseBodyAsString(),
            new HttpHeaders(), ex.getStatusCode(), request);

    }

}

这会导致ResponseEntity具有来自微服务中的Exception的正确HttpStatus以及包含前端所需消息的JSON正文。