Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 12111

Create custom Response when APIs path is wrong in Spring boot application instead of getting entire spring stack trace

$
0
0

Issue:
I am looking for your help to figure out an issue I am getting. I have designed an ErrorRenderer.class to handle wrong API paths (HTTPStatus.NOT_FOUND exceptions) and return a custom response like this:

{"title": "NOT FOUND","status": 404,"message": "Given path '/service/abcd' does not exist.."}

but instead of getting a custom response, I am getting the below response (spring default stack trace) when API path is wrong:

{"cause": null,"stackTrace": [        {"classLoaderName": "app","moduleName": null,"moduleVersion": null,"methodName": "lambda$handle$1","fileName": "ResourceWebHandler.java","lineNumber": 431,"className": "org.springframework.web.reactive.resource.ResourceWebHandler","nativeMethod": false        }    ],"type": "about:blank","title": "Not Found","status": {"reasonPhrase": "Not Found","statusCode": 404    },"detail": "404 NOT_FOUND \"No static resource service/abcd.\"","instance": null,"parameters": {},"message": "Not Found: 404 NOT_FOUND \"No static resource service/abcd.\"","suppressed": [],"localizedMessage": "Not Found: 404 NOT_FOUND \"No static resource service/abcd.\""}

Information about my classes:

I have given Highest Order to ErrorRenderer.class-

@Component@Order(-3)public class ErrorRenderer extends AbstractErrorWebExceptionHandler {  public ErrorRenderer(      ErrorAttributes errorAttributes,      WebProperties.Resources resources,      ApplicationContext applicationContext,      ServerCodecConfigurer serverCodecConfigurer) {    super(errorAttributes, resources, applicationContext);    super.setMessageWriters(serverCodecConfigurer.getWriters());    super.setMessageReaders(serverCodecConfigurer.getReaders());  }  @Override  protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {    return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);  }  private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {    Map<String, Object> errorPropertiesMap =        getErrorAttributes(request, ErrorAttributeOptions.defaults());    HttpStatus status = HttpStatus.BAD_REQUEST;    if (HttpStatus.NOT_FOUND.value() == (int) errorPropertiesMap.get("status")) {      errorPropertiesMap.clear();      errorPropertiesMap.put("title", "NOT FOUND");      errorPropertiesMap.put("status", HttpStatus.NOT_FOUND.value());      errorPropertiesMap.put("message",          String.format("Given path '%s' does not exist..", request.requestPath().toString()));      status = HttpStatus.NOT_FOUND;    }    return ServerResponse.status(status)        .contentType(MediaType.APPLICATION_JSON)        .body(BodyInserters.fromValue(errorPropertiesMap));  }}

and there is a Config class for ErrorRenderer.class:

@Configurationpublic class WebPropertiesConfiguration {  @Bean  public WebProperties.Resources resources() {    return new WebProperties.Resources();  }}

I have another class to handle other exceptions:

@ControllerAdvice@Slf4j@Order(-1)public class RestExceptionHandler    implements HttpAdviceTrait, GeneralAdviceTrait, ValidationAdviceTrait {  @ExceptionHandler({ServerWebInputException.class})  public Mono<ResponseEntity<Object>> error(ServerWebInputException e) {    some code .....    return Mono.just(ResponseEntity.badRequest().body(message));  } @ExceptionHandler(ValidationException.class)  public Mono<ResponseEntity<Problem>> validationEx(      some code....    return create(exception, map(errorResponse, Status.FORBIDDEN), request);  }  @ExceptionHandler(WebClientResponseException.class)  public Mono<ResponseEntity<Problem>> restException(      some code....  }  private Problem map(ErrorResponse response, Status status) {    return Problem.builder()        .withTitle(response.getCode().toLowerCase().replaceAll("_", " "))        .withDetail(response.getMessage())        .withStatus(status)        .build();  }}

there is another Configuration class for RestExceptionHandler.class

@Slf4j@Configurationpublic class RestExceptionHandlerConfiguration {  @Bean  public ProblemModule problemModule() {    return new ProblemModule();  }  @Bean  @Order(-2)  public WebExceptionHandler problemExceptionHandler(          ObjectMapper mapper, RestExceptionHandler problemHandling) {    return new ProblemExceptionHandler(mapper, problemHandling);  }  @Bean  public ConstraintViolationProblemModule constraintViolationProblemModule() {    return new ConstraintViolationProblemModule();  }}

Routing Test:

I have written a routing test. Just to test I commented out RestExceptionHandler.class and see if the ErrorRenderer.class would get picked up or not and it gets picked up and the test passes. But since I need to find a way to have both RestExceptionHandler.class and ErrorRenderer.class together at the end (commenting out solution will not work). And when the RestExceptionHandler.class is present then the test fails as somehow the RestExceptionHandler.class is getting picked up before the ErrorRenderer.class even though I haveprovided the highest order to ErrorRenderer.class -> @Order(-3).

@Test  void path() {    webClient        .get()        .uri("/service/abcd")        .exchange()        .expectStatus()        .isNotFound()        .expectBody()        .jsonPath("$.title")        .isEqualTo("NOT FOUND")        .jsonPath("$.status")        .isEqualTo(404)        .jsonPath("$.message")        .isEqualTo("Given path '/service/abcd' does not exist..");  }

when the test is failing, I get this on the console (JSON path issue does not make much sense though, I guess important is log coming form AdviceTraits):

o.z.problem.spring.common.AdviceTraits   : Not Found: 404 NOT_FOUND "No static resource service/abcd."At JSON path "$.status", value <{statusCode=404, reasonPhrase=Not Found}> of type <java.util.LinkedHashMap> cannot be converted to type <java.lang.Integer>java.lang.AssertionError: At JSON path "$.status", value <{statusCode=404, reasonPhrase=Not Found}> of type <java.util.LinkedHashMap> cannot be converted to type <java.lang.Integer>    ......................

o.z.problem.spring.common.AdviceTraits : Not Found: 404 NOT_FOUND "No static resource service/abcd." - I believe, This is coming from RestExceptionHandler.class

change in application.properties:

I have also tried to add these into aplication.properties but no results:

org.springframework.reactive.function.server.RouterFunctions=INFOserver.error.whitelabel.enabled: false

Specifications:I am using Spring Boot 3.2 with Java 17.

Note:I recently upgraded spring boot version from 2.6 to 3.2, could this be the reason for the issue? custom response was working before with the older spring version but not anymore with the new spring version.

I could use some insight into what is going wrong here.

Thank you in advance.


Viewing all articles
Browse latest Browse all 12111

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>