Zuul 예외 사용자 지정
Zuul에서 URL이 라우팅되는 서비스도 다운될 수 있는 시나리오가 있습니다.그러면 응답 본문이 JSON 본문 응답에 500 HTTP Status 및 ZuulException으로 느려집니다.
{
"timestamp": 1459973637928,
"status": 500,
"error": "Internal Server Error",
"exception": "com.netflix.zuul.exception.ZuulException",
"message": "Forwarding error"
}
JSON 응답을 커스터마이즈 또는 삭제하거나 HTTP 상태 코드를 변경하는 것 뿐입니다.
@ControllerAdvice를 사용하여 예외 핸들러를 생성하려고 했지만 핸들러가 예외를 포착하지 못했습니다.
갱신:
그래서 Zuul 필터를 확장했습니다.오류 실행 후 실행 메서드로 이행하는 것을 확인할 수 있습니다.그때 응답을 변경하는 방법은 무엇입니까?아래는 지금까지 제가 알아낸 것입니다.SendErrorFilter에 대해 어디서 읽었는데 어떻게 구현하고 어떤 기능을 합니까?
public class CustomFilter extends ZuulFilter {
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
final RequestContext ctx = RequestContext.getCurrentContext();
final HttpServletResponse response = ctx.getResponse();
if (HttpStatus.INTERNAL_SERVER_ERROR.value() == ctx.getResponse().getStatus()) {
try {
response.sendError(404, "Error Error"); //trying to change the response will need to throw a JSON body.
} catch (final IOException e) {
e.printStackTrace();
} ;
}
return null;
}
이것을 @EnableZuulProxy가 있는 클래스에 추가했습니다.
@Bean
public CustomFilter customFilter() {
return new CustomFilter();
}
[동료 중 한 명이 코드화] 드디어 이 작업을 완료했습니다.-
public class CustomErrorFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(CustomErrorFilter.class);
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return -1; // Needs to run before SendErrorFilter which has filterOrder == 0
}
@Override
public boolean shouldFilter() {
// only forward to errorPath if it hasn't been forwarded to already
return RequestContext.getCurrentContext().containsKey("error.status_code");
}
@Override
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
Object e = ctx.get("error.exception");
if (e != null && e instanceof ZuulException) {
ZuulException zuulException = (ZuulException)e;
LOG.error("Zuul failure detected: " + zuulException.getMessage(), zuulException);
// Remove error code to prevent further error handling in follow up filters
ctx.remove("error.status_code");
// Populate context with new response values
ctx.setResponseBody(“Overriding Zuul Exception Body”);
ctx.getResponse().setContentType("application/json");
ctx.setResponseStatusCode(500); //Can set any error code as excepted
}
}
catch (Exception ex) {
LOG.error("Exception filtering in custom error filter", ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
}
Zuul Request Context에 포함되어 있지 않습니다.error.exception
이 답변에 언급된 바와 같이
최신 Zuul 오류 필터:
@Component
public class ErrorFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(ErrorFilter.class);
private static final String FILTER_TYPE = "error";
private static final String THROWABLE_KEY = "throwable";
private static final int FILTER_ORDER = -1;
@Override
public String filterType() {
return FILTER_TYPE;
}
@Override
public int filterOrder() {
return FILTER_ORDER;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
final RequestContext context = RequestContext.getCurrentContext();
final Object throwable = context.get(THROWABLE_KEY);
if (throwable instanceof ZuulException) {
final ZuulException zuulException = (ZuulException) throwable;
LOG.error("Zuul failure detected: " + zuulException.getMessage());
// remove error code to prevent further error handling in follow up filters
context.remove(THROWABLE_KEY);
// populate context with new response values
context.setResponseBody("Overriding Zuul Exception Body");
context.getResponse().setContentType("application/json");
// can set any error code as excepted
context.setResponseStatusCode(503);
}
return null;
}
}
나도 같은 문제가 있어서 더 쉽게 해결할 수 있었다.
이 필터만 넣어주세요.run()
방법
if (<your condition>) {
ZuulException zuulException = new ZuulException("User message", statusCode, "Error Details message");
throw new ZuulRuntimeException(zuulException);
}
그리고.SendErrorFilter
원하는 메시지를 사용자에게 전달합니다.statusCode
.
예외 패턴의 이 예외는 보기 좋게 보이지는 않지만 여기서 작동합니다.
전송은 많은 경우 필터에 의해 이루어집니다.이 경우 요구는 컨트롤러에 도달하지 않습니다.이것이 @ControllerAdvice가 작동하지 않는 이유를 설명해 줍니다.
컨트롤러로 전송하면 @ControllerAdvice가 동작합니다.spring이 @ControllerAdvice에서 주석을 붙인 클래스의 인스턴스를 만들 수 있는지 확인합니다.그 때문에, 클래스에 브레이크 포인트를 배치해, 적중 여부를 확인합니다.
전송이 발생하는 컨트롤러 방식에도 브레이크 포인트를 추가합니다.검사 이외의 컨트롤러 메서드를 잘못 호출했을 가능성이 있습니다.
이러한 순서는, 문제의 해결에 도움이 됩니다.
@ControllerAdvice로 주석을 단 클래스에 @ExceptionHandler(Exception.class)로 주석을 단 ExceptionHandler 메서드를 추가합니다.이 메서드는 모든 예외를 검출해야 합니다.
EDIT : Zuulfilter에서 반환된 오류 응답을 변환하는 자체 필터를 추가할 수 있습니다.여기서 원하는 대로 응답을 변경할 수 있습니다.
에러 응답을 커스터마이즈 하는 방법에 대해서는, 다음과 같이 설명합니다.
필터를 올바르게 설치하는 것은 조금 어려울 수 있습니다.올바른 위치에 대해서는 정확히 알 수 없지만 필터의 순서와 예외를 처리하는 위치를 알고 있어야 합니다.
Zuulfilter 앞에 배치하는 경우 doFilter()를 호출한 후 오류 처리를 코드화해야 합니다.
Zuulfilter 뒤에 배치할 경우 doFilter()를 호출하기 전에 오류 처리를 코드화해야 합니다.
doFilter() 전후로 필터에 브레이크 포인트를 추가하면 올바른 위치를 찾을 수 있습니다.
다음은 @ControllerAdvice를 사용하여 수행하는 절차입니다.
- '필터' 를 추가합니다.
error
.SendErrorFilter
주울 그 자체. - 에서 .
RequestContext
SendErrorFilter
실행에서 제외됩니다. RequestDispatcher
를ErrorController
- 하고 @RestController 클래스를 합니다.
AbstractErrorController
를 다시 필터를 합니다). (키, 예외로 (키, 예외로).RequestContext
컨트롤러에 있습니다).
이것으로 예외가 @ControllerAdvice 클래스에서 검출됩니다.
The simplest solution is to follow first 4 steps.
1. Create your own CustomErrorController extends
AbstractErrorController which will not allow the
BasicErrorController to be called.
2. Customize according to your need refer below method from
BasicErrorController.
<pre><code>
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
</pre></code>
4. You can control whether you want exception / stack trace to be printed or not can do as mentioned below:
<pre><code>
server.error.includeException=false
server.error.includeStacktrace=ON_TRACE_PARAM
</pre></code>
====================================================
5. If you want all together different error response re-throw your custom exception from your CustomErrorController and implement the Advice class as mentioned below:
<pre><code>
@Controller
@Slf4j
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
log.info("Created");
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
throw new CustomErrorException(String.valueOf(status.value()), status.getReasonPhrase(), body);
}
}
@ControllerAdvice
public class GenericExceptionHandler {
// Exception handler annotation invokes a method when a specific exception
// occurs. Here we have invoked Exception.class since we
// don't have a specific exception scenario.
@ExceptionHandler(CustomException.class)
@ResponseBody
public ErrorListWsDTO customExceptionHandle(
final HttpServletRequest request,
final HttpServletResponse response,
final CustomException exception) {
LOG.info("Exception Handler invoked");
ErrorListWsDTO errorData = null;
errorData = prepareResponse(response, exception);
response.setStatus(Integer.parseInt(exception.getCode()));
return errorData;
}
/**
* Prepare error response for BAD Request
*
* @param response
* @param exception
* @return
*/
private ErrorListWsDTO prepareResponse(final HttpServletResponse response,
final AbstractException exception) {
final ErrorListWsDTO errorListData = new ErrorListWsDTO();
final List<ErrorWsDTO> errorList = new ArrayList<>();
response.setStatus(HttpStatus.BAD_REQUEST.value());
final ErrorWsDTO errorData = prepareErrorData("500",
"FAILURE", exception.getCause().getMessage());
errorList.add(errorData);
errorListData.setErrors(errorList);
return errorListData;
}
/**
* This method is used to prepare error data
*
* @param code
* error code
* @param status
* status can be success or failure
* @param exceptionMsg
* message description
* @return ErrorDTO
*/
private ErrorWsDTO prepareErrorData(final String code, final String status,
final String exceptionMsg) {
final ErrorWsDTO errorDTO = new ErrorWsDTO();
errorDTO.setReason(code);
errorDTO.setType(status);
errorDTO.setMessage(exceptionMsg);
return errorDTO;
}
}
</pre></code>
이게 나한테 효과가 있었어.RestExceptionResponse는 @ControllerAdvice 내에서 사용되는 클래스이므로 내부 ZuulExceptions의 경우에도 동일한 예외 응답이 있습니다.
@Component
@Log4j
public class CustomZuulErrorFilter extends ZuulFilter {
private static final String SEND_ERROR_FILTER_RAN = "sendErrorFilter.ran";
@Override
public String filterType() {
return ERROR_TYPE;
}
@Override
public int filterOrder() {
return SEND_ERROR_FILTER_ORDER - 1; // Needs to run before SendErrorFilter which has filterOrder == 0
}
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
Throwable ex = ctx.getThrowable();
return ex instanceof ZuulException && !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
}
@Override
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
ZuulException ex = (ZuulException) ctx.getThrowable();
// log this as error
log.error(StackTracer.toString(ex));
String requestUri = ctx.containsKey(REQUEST_URI_KEY) ? ctx.get(REQUEST_URI_KEY).toString() : "/";
RestExceptionResponse exceptionResponse = new RestExceptionResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex, requestUri);
// Populate context with new response values
ctx.setResponseStatusCode(500);
this.writeResponseBody(ctx.getResponse(), exceptionResponse);
ctx.set(SEND_ERROR_FILTER_RAN, true);
}
catch (Exception ex) {
log.error(StackTracer.toString(ex));
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
private void writeResponseBody(HttpServletResponse response, Object body) throws IOException {
response.setContentType("application/json");
try (PrintWriter writer = response.getWriter()) {
writer.println(new JSonSerializer().toJson(body));
}
}
}
출력은 다음과 같습니다.
{
"timestamp": "2020-08-10 16:18:16.820",
"status": 500,
"error": "Internal Server Error",
"path": "/service",
"exception": {
"message": "Filter threw Exception",
"exceptionClass": "com.netflix.zuul.exception.ZuulException",
"superClasses": [
"com.netflix.zuul.exception.ZuulException",
"java.lang.Exception",
"java.lang.Throwable",
"java.lang.Object"
],
"stackTrace": null,
"cause": {
"message": "com.netflix.zuul.exception.ZuulException: Forwarding error",
"exceptionClass": "org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException",
"superClasses": [
"org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException",
"java.lang.RuntimeException",
"java.lang.Exception",
"java.lang.Throwable",
"java.lang.Object"
],
"stackTrace": null,
"cause": {
"message": "Forwarding error",
"exceptionClass": "com.netflix.zuul.exception.ZuulException",
"superClasses": [
"com.netflix.zuul.exception.ZuulException",
"java.lang.Exception",
"java.lang.Throwable",
"java.lang.Object"
],
"stackTrace": null,
"cause": {
"message": "Load balancer does not have available server for client: template-scalable-service",
"exceptionClass": "com.netflix.client.ClientException",
"superClasses": [
"com.netflix.client.ClientException",
"java.lang.Exception",
"java.lang.Throwable",
"java.lang.Object"
],
"stackTrace": null,
"cause": null
}
}
}
}
}
언급URL : https://stackoverflow.com/questions/36461493/customizing-zuul-exception
'source' 카테고리의 다른 글
ResponseEntity를 사용하여 InputStream을 닫는 적절한 스트리밍 방법 (0) | 2023.02.26 |
---|---|
리액트 라우터 v4 - GET할 수 없음 *url* (0) | 2023.02.26 |
날짜별로 MongoDB ObjectId를 조회할 수 있습니까? (0) | 2023.02.26 |
사용자 지정 게시 유형을 표시하는 워드프레스 검색 중지 (0) | 2023.02.26 |
iPhone용 Objective-C에서 Google Directions API polylines 필드를 lat long 포인트로 디코딩하는 방법은 무엇입니까? (0) | 2023.02.26 |