[JAVA] Personnalisation minimale de la page d'erreur Spring Boot (implémentation de l'interface ErrorController)

Aperçu

Exemple d'implémentation de page d'erreur minimale

Lorsqu'une erreur se produit, elle s'affiche sous la forme 404 Not Found.

Classe d'implémentation minimale de l'interface ErrorController

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

/**
 *Contrôleur d'erreur pour l'ensemble de l'application Web.
 *Classe d'implémentation d'interface ErrorController.
 */
@Controller
@RequestMapping("/error") //Mappage vers la page d'erreur
public class MySimpleErrorController implements ErrorController {

  /**
   *Renvoie le chemin de la page d'erreur.
   *
   * @return Chemin de la page d'erreur
   */
  @Override
  public String getErrorPath() {
    return "/error";
  }

  /**
   *Renvoie un objet ModelAndView pour la réponse.
   *
   * @param req demande d'informations
   * @informations de réponse param mav
   * @Objet ModelAndView pour la réponse HTML de retour
   */
  @RequestMapping
  public ModelAndView error(HttpServletRequest req, ModelAndView mav) {

    //404 introuvable pour une erreur
    //Le code du stator et le contenu de sortie peuvent être personnalisés selon les besoins.
    mav.setStatus(HttpStatus.NOT_FOUND);

    //Spécifiez le nom de la vue
    //Modèle Thymeleaf src/main/resources/templates/error.Utilisez html
    mav.setViewName("error");

    return mav;
  }
}

Fichier de modèle HTML Thymeleaf src / main / resources / templates / error.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>404 Not Found</title>
</head>
<body>
<h1>404 Not Found</h1>
</body>
</html>

Vérification du fonctionnement

404 Not Found est maintenant renvoyé au client au lieu de 500 Internal Server Error etc. lorsqu'une erreur se produit. Il renvoie également désormais HTML au lieu de JSON pour l'accès à partir de clients machine tels que curl.

$ curl --include http://localhost:8080/
HTTP/1.1 404 
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: text/html;charset=UTF-8
Content-Language: ja-JP
Transfer-Encoding: chunked
Date: Mon, 18 Nov 2019 13:33:52 GMT

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>404 Not Found</title>
</head>
<body>
<h1>404 Not Found</h1>
</body>
</html>

Exemple d'implémentation de page d'erreur qui génère des informations d'erreur détaillées

Créez des informations d'erreur détaillées afin de pouvoir sélectionner les éléments à renvoyer au client et les afficher dans le journal.

Classe DefaultErrorAttributes

La classe DefaultErrorAttributes de Spring Boot facilite la création d'informations détaillées sur les erreurs.

DefaultErrorAttributes (Spring Boot Docs 2.2.1.RELEASE API)

Default implementation of ErrorAttributes. Provides the following attributes when possible:

・ Horodatage - L'heure à laquelle les erreurs ont été extraites ・ Statut - Le code d'état ・ Erreur - La raison de l'erreur ・ Exception - Le nom de classe de l'exception racine (si configuré) ・ Message - Le message d'exception ・ Erreurs - Tout objet ObjectErrors d'une exception BindingResult ・ Trace - La trace de pile d'exceptions ・ Chemin - Le chemin de l'URL lorsque l'exception a été déclenchée

Classe d'implémentation d'interface ErrorController

import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 *Contrôleur d'erreur pour l'ensemble de l'application Web.
 *Classe d'implémentation d'interface ErrorController.
 */
@Controller
@RequestMapping("/error") //Mappage vers la page d'erreur
public class MyDetailErrorController implements ErrorController {

  /**
   *Renvoie le chemin de la page d'erreur.
   *
   * @return Chemin de la page d'erreur
   */
  @Override
  public String getErrorPath() {
    return "/error";
  }

  /**
   *Extraire les informations d'erreur.
   *
   * @param req demande d'informations
   * @renvoyer les informations d'erreur
   */
  private static Map<String, Object> getErrorAttributes(HttpServletRequest req) {
    //Obtenez des informations d'erreur détaillées avec la classe DefaultErrorAttributes
    ServletWebRequest swr = new ServletWebRequest(req);
    DefaultErrorAttributes dea = new DefaultErrorAttributes(true);
    return dea.getErrorAttributes(swr, true);
  }

  /**
   *Déterminez l'état HTTP de la réponse.
   *
   * @param req demande d'informations
   * @Statut HTTP pour la réponse de retour
   */
  private static HttpStatus getHttpStatus(HttpServletRequest req) {
    //Déterminer l'état HTTP
    //Ici, tous sauf 404 sont réglés sur 500.
    Object statusCode = req.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
    HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
    if (statusCode != null && statusCode.toString().equals("404")) {
      status = HttpStatus.NOT_FOUND;
    }
    return status;
  }

  /**
   *Renvoie un objet ModelAndView pour la réponse HTML.
   *
   * @param req demande d'informations
   * @informations de réponse param mav
   * @Objet ModelAndView pour la réponse HTML de retour
   */
  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
  public ModelAndView myErrorHtml(HttpServletRequest req, ModelAndView mav) {

    //Obtenir des informations d'erreur
    Map<String, Object> attr = getErrorAttributes(req);

    //Déterminer l'état HTTP
    HttpStatus status = getHttpStatus(req);

    //Définir l'état HTTP
    mav.setStatus(status);

    //Spécifiez le nom de la vue
    //Modèle Src pour Thymeleaf/main/resources/templates/error.html
    mav.setViewName("error");

    //Définissez les informations que vous souhaitez afficher
    mav.addObject("status", status.value());
    mav.addObject("timestamp", attr.get("timestamp"));
    mav.addObject("error", attr.get("error"));
    mav.addObject("exception", attr.get("exception"));
    mav.addObject("message", attr.get("message"));
    mav.addObject("errors", attr.get("errors"));
    mav.addObject("trace", attr.get("trace"));
    mav.addObject("path", attr.get("path"));

    return mav;
  }

  /**
   *Renvoie un objet ResponseEntity pour la réponse JSON.
   *
   * @param req demande d'informations
   * @Objet ResponseEntity pour renvoyer la réponse JSON
   */
  @RequestMapping
  public ResponseEntity<Map<String, Object>> myErrorJson(HttpServletRequest req) {

    //Obtenir des informations d'erreur
    Map<String, Object> attr = getErrorAttributes(req);

    //Déterminer l'état HTTP
    HttpStatus status = getHttpStatus(req);

    //Définissez les informations que vous souhaitez afficher
    Map<String, Object> body = new HashMap();
    body.put("status", status.value());
    body.put("timestamp", attr.get("timestamp"));
    body.put("error", attr.get("error"));
    body.put("exception", attr.get("exception"));
    body.put("message", attr.get("message"));
    body.put("errors", attr.get("errors"));
    body.put("trace", attr.get("trace"));
    body.put("path", attr.get("path"));

    //Informations de sortie dans JSON
    return new ResponseEntity<>(body, status);
  }
}

Fichier de modèle HTML Thymeleaf src / main / resources / templates / error.html

Renvoie la valeur spécifiée dans la classe d'implémentation d'interface ErrorController.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>/error</title>
</head>
<body>
<div th:text="'timestamp: ' + ${timestamp}"></div>
<div th:text="'status: ' + ${status}"></div>
<div th:text="'error: ' + ${error}"></div>
<div th:text="'exception: ' + ${exception}"></div>
<div th:text="'message: ' + ${message}"></div>
<div th:text="'errors: ' + ${errors}"></div>
<div th:text="'trace: ' + ${trace}"></div>
<div th:text="'path: ' + ${path}"></div>
</body>
</html>

Vérification du fonctionnement

Un exemple de renvoi d'une erreur de serveur interne 500 en HTML. Des informations détaillées sur les erreurs sont intégrées au HTML.

$ curl --include -H "accept: text/html" http://localhost:8080/sample
HTTP/1.1 500 
Content-Type: text/html;charset=UTF-8
Content-Language: ja-JP
Transfer-Encoding: chunked
Date: Mon, 18 Nov 2019 13:55:21 GMT
Connection: close

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>/error</title>
</head>
<body>
<div>timestamp: Mon Nov 18 22:55:21 JST 2019</div>
<div>status: 500</div>
<div>error: Internal Server Error</div>
<div>exception: java.lang.RuntimeException</div>
<div>message: This is a sample exception.</div>
<div>errors: null</div>
<div>trace: java.lang.RuntimeException: This is a sample exception.
	at com.example.demo.DemoApplication.index(DemoApplication.java:18)
(Ce qui suit est omis)

Un exemple de renvoi d'une erreur de serveur interne 500 dans JSON. Des informations détaillées sur l'erreur sont incluses dans le JSON.

$ curl --include http://localhost:8080/sample
HTTP/1.1 500 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 18 Nov 2019 13:55:31 GMT
Connection: close

{"exception":"java.lang.RuntimeException","path":"/sample","trace":"java.lang.RuntimeException: This is a sample exception.\n\tat com.example.demo.DemoApplication.index(DemoApplication.java:18)\n\t(Omission)java.base/java.lang.Thread.run(Thread.java:834)\n","error":"Internal Server Error","message":"This is a sample exception.","errors":null,"status":500,"timestamp":"2019-11-18T13:55:31.644+0000"}

Matériel de référence

Recommended Posts

Personnalisation minimale de la page d'erreur Spring Boot (implémentation de l'interface ErrorController)
Page d'erreur Spring Boot Whitelabel et réponse JSON
[FCM] Implémentation de la transmission de messages en utilisant FCM + Spring boot
Je veux contrôler le message d'erreur par défaut de Spring Boot
Erreur inconnue dans la ligne 1 de pom.xml lors de l'utilisation de Spring Boot dans Eclipse
Mémorandum lorsque Spring Boot 1.5.10 → Spring Boot 2.0.0
Méthode de résolution d'erreur Spring Boot + PostgreSQL
Sortie de message (Spring boot)
Erreur de mise en œuvre lors de la mise en œuvre de la validation Spring
Mémorandum WebMvcConfigurer de Spring Boot 2.0 (printemps 5)
Erreur Javaw.exe lors du démarrage de Spring Boot (STS)
À partir de Spring Boot 2.3, la page d'erreur par défaut ne contient plus d'informations détaillées sur l'erreur