Skip to content

Instantly share code, notes, and snippets.

@jonikarppinen
Last active June 16, 2021 02:19
Show Gist options
  • Star 55 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save jonikarppinen/662c38fb57a23de61c8b to your computer and use it in GitHub Desktop.
Save jonikarppinen/662c38fb57a23de61c8b to your computer and use it in GitHub Desktop.
Example of replacing Spring Boot "whitelabel" error page with custom error responses (with JSON response body)
package com.company.project.controllers;
import com.company.project.model.api.ErrorJson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* Based on the helpful answer at http://stackoverflow.com/q/25356781/56285,
* with error details in response body added.
*
* @author Joni Karppinen
* @since 20.2.2015
*/
@RestController
public class CustomErrorController implements ErrorController {
private static final String PATH = "/error";
@Value("${debug}")
private boolean debug;
@Autowired
private ErrorAttributes errorAttributes;
@RequestMapping(value = PATH)
ErrorJson error(HttpServletRequest request, HttpServletResponse response) {
// Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
// Here we just define response body.
return new ErrorJson(response.getStatus(), getErrorAttributes(request, debug));
}
@Override
public String getErrorPath() {
return PATH;
}
private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
}
}
@joshuaherman
Copy link

joshuaherman commented Dec 22, 2017

package io.apptrade.spring.controller;

import io.apptrade.spring.model.ErrorJson;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@log
@RestController
@propertysource("classpath:application.properties") //custom-error-controller.debug place in file and set true/false
@configuration
public class CustomErrorController implements ErrorController{

private static final String PATH = "/error";

@Value("${custom-error-controller.debug}")
private boolean debug;

@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
    return new PropertySourcesPlaceholderConfigurer();
}

@Autowired
private ErrorAttributes errorAttributes;

@RequestMapping(value = PATH)
ResponseEntity<ErrorJson> error(HttpServletRequest request, HttpServletResponse response){
     return ResponseEntity.status(response.getStatus())
         .body(
             new ErrorJson(response.getStatus(), getErrorAttributes(request, debug)
         )
     );
}

@Override
public String getErrorPath(){
    return PATH;
}

private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
    RequestAttributes requestAttributes = new ServletRequestAttributes(request);
    return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
}

}

package io.apptrade.spring.model;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.stereotype.Component;

import java.util.Map;

@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
@getter(AccessLevel.PUBLIC)
@Setter(AccessLevel.PROTECTED)
@NoArgsConstructor
@component
public class ErrorJson{

private Integer status;
private String error;

@JsonIgnore
private String message;
private String timeStamp;
private String trace;

public ErrorJson(int status, Map<String, Object> errorAttributes) {
    this.status = status;
    this.error = (String) errorAttributes.get("error");
    this.message = (String) errorAttributes.get("message");
    this.timeStamp = errorAttributes.get("timestamp").toString();
    this.trace = (String) errorAttributes.get("trace");
}

}
THIS Works!! and has been tested. Produces JSON

@Swis2kk
Copy link

Swis2kk commented Jun 6, 2018

Thank you very much!

@HamedNet
Copy link

Hi
i get this error:

java: cannot access org.springframework.web.reactive.function.server.ServerRequest
class file for org.springframework.web.reactive.function.server.ServerRequest not found

@Vladis466
Copy link

Slightly change the class to use webrequest

`public class CustomErrorController implements ErrorController {

private static final String PATH = "/error";

@Value("${custom-error-controller.debug}")
private boolean debug;

@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
    return new PropertySourcesPlaceholderConfigurer();
}

@Autowired
private ErrorAttributes errorAttributes;

@RequestMapping(value = PATH)
ResponseEntity<ErrorJson> error(WebRequest webRequest, HttpServletResponse response){
    return ResponseEntity.status(response.getStatus())
            .body(
                    new ErrorJson(response.getStatus(), getErrorAttributes(webRequest, debug)
                    )
            );
}

@Override
public String getErrorPath(){
    return PATH;
}

private Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
    return errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}

}`

@vanduc1102
Copy link

vanduc1102 commented Sep 19, 2018

here is my implementation:
File: application.properties
server.error.whitelabel.enabled=false
File: OverrideErrorWhitePage.java

@Controller
public class OverrideErrorWhitePage implements ErrorController {
  private static final String ERROR_PATH = "/error";

  @ResponseBody
  @ResponseStatus(HttpStatus.NOT_FOUND)
  @RequestMapping(value = ERROR_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
  public EcatalogGenericResponse handleError() {
    return ResponseBodyBuilder.notFoundHandler();
  }

  @Override
  public String getErrorPath() {
    return ERROR_PATH;
  }
}

File:App.java

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

File: EcatalogGenericResponse.java

public class EcatalogGenericResponse<T> {
  private String responseCode;
  private String responseMessage;
  private T data;
}

@DPSingh9900
Copy link

DPSingh9900 commented Oct 3, 2018

I want to know how to send error response to custom page in Latest Spring Boot. I tried below mention code but its converts message to JSON format.

@RestController
public class MyCustomErrorHandlerController implements ErrorController {

private static final String PATH = "/error";

@Value("${debug}")
private boolean debug;

@Autowired
private ErrorAttributes errorAttributes;

@RequestMapping(value = PATH)
ErrorJson error(HttpServletRequest request, HttpServletResponse response) {
    // Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring. 
    // Here we just define response body.
	//return new Error(response.getStatus(), getErrorAttributes(request, debug));
    return new ErrorJson(response.getStatus(), getErrorAttributes(request, debug));
}

@Override
public String getErrorPath() {
    return PATH;
}

private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
	WebRequest webRequest = new ServletWebRequest(request);
    return errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}

}

@vijaymohanp
Copy link

I am getting HTTP ERROR 406 code. Anyone has this problem before, at server side everything looks good, but client rejecting the request. I am thinking it is because, browser expecting html and from the server we are trying to send json response?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment