2017-10-16 146 views
3

我們有一個微服務體系結構。每項服務都通過Rest展示數據。所有控制器都使用Spring進行設置:使用RestTemplate的端點到端點的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); 
    } 

} 

在郵遞員可以看到以下JSON異常後,上述被拋出

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

我們有4個不同的服務,像這些都設立了同樣的方式。

我們的用戶界面是在Angular 4中製作的,並使REST調用我們的網關。網關是微服務與用戶界面之間的連接。它還暴露了一個REST端點。它也是用Spring實現的。我加了澄清一個畫面:

architecture

「編輯:我看到,我沒有完成的箭當然,所有數據傳回至用戶界面。」

問題

網關使用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消息中。我不想要這種行爲,我希望它將原始消息傳遞給用戶界面。

有關如何控制此行爲的任何想法?

注意

我與郵差測試,如果你直接訪問該微服務則返回409寫在自定義異常

我已經嘗試重寫泉ResponseErrorHandler的消息,但無法以這種方式找到合適的解決方案。

+1

那麼,網關的代碼是什麼? –

+0

正如我所說的,它使用Spring進行設置(因此與微服務相同),但使用RestTemplate調用微服務的API – appel500

回答

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; 
    } 
+0

感謝您的快速響應!我嘗試了你的解決方案,但是這導致了相同格式的錯誤。 – appel500

+0

你能分享一下你試過的代碼嗎? –

0

問題可以被關閉。

解決方案是將發生在微服務中的異常映射到網關中的有效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); 

    } 

} 

這導致具有正確的HTTPStatus從微服務異常,並含有由前端所需要的消息的JSON體ResponseEntity這樣做。