[SpringBoot] Une analyse vernaculaire du code source du mécanisme de gestion des erreurs par défaut et une configuration personnalisée (dans le cas d'une fosse)

Préface

Version SpringBoot: v2.2.6.RELEASE

Ce blog résume principalement les 3 aspects suivants:

  • One, page d'erreur SpringBoot et données
  • 2. Introduction de classes apparentées
  • Trois, suivi du code source, le flux du mécanisme de traitement par défaut
  • Quatre, données d'erreur personnalisées

Du point de vue des apprenants, ce blog présente clairement et clairement toute la hiérarchie et les idées avec des langues, des images et des textes populaires. Je crois que si vous finissez de le lire patiemment, vous devriez avoir une récolte différente. Le code source de tout framework est un système énorme. La lecture du code source ne nécessite pas une compréhension exacte de chaque détail du code, mais doit prêter attention et comprendre l'idée principale du code principal (clé). Enfin, pourquoi ne pas aimer la collection après l'avoir lue? J'ai passé la majeure partie de la journée à résumer, et je sens que j'ai beaucoup gagné, et j'ai une compréhension plus claire et plus profonde de SpringBoot.

One, page d'erreur SpringBoot et données

1. Le visiteur est un navigateur

2. Le visiteur est un autre client (ici, j'utilise Postman pour tester)

3. Comment distinguer les visiteurs? Demande d'en-tête!

Le visiteur est un navigateur:

Le visiteur est un autre client:

Du point de vue ci-dessus, nous pouvons diviser la gestion des erreurs de SpringBoot en deux catégories:

(1) Le visiteur est un navigateur et une page d'erreur correspondante est renvoyée

(2) Si le visiteur est un client autre que le navigateur, Duan renverra les données Json correspondantes

 

2. Introduction de classes apparentées

Pour les classes clés ici, seulement une brève introduction, il vous suffit de connaître le rôle principal que cette classe joue dans l'ensemble du mécanisme de gestion des erreurs par défaut. Dans la quatrième partie de la description du processus, nous allons concevoir ces classes en profondeur.

ErrorMvcAutoConfiguration : comme son nom l'indique, il s'agit d'une classe de configuration automatique du mécanisme de gestion des erreurs

En regardant le code source, on constate que cette classe ajoute les 3 composants importants suivants liés au mécanisme de gestion des erreurs au conteneur:

 

1. ErrorPageCustomizer : Personnalisez les règles de génération d'erreurs et générez / demandes d'erreur

2. BasicErrorController : comme son nom l'indique, c'est un contrôleur qui traite / demandes d'erreur

(1) Revenir à la page Html

(2) Renvoyer les données Json

3. DefaultErrorViewResolver : résolution de la vue d'erreur

4. DefaultErrorAttributes : partager des informations sur les erreurs

 

Trois, suivi du code source, le flux du mécanisme de traitement par défaut

Prenez la page Html de réponse comme exemple

1. Comme vous pouvez le voir dans l'introduction ci-dessus, lorsqu'une page d'erreur est renvoyée, cette méthode de BasicErrorController sera appelée (voir la figure ci-dessous).

Dans cette méthode, obtenez le code d'état demandé et certaines données de modèle (la méthode getErrorAttributes () est également très importante, qui sera introduite plus tard), puis appelez la méthode resolErrorView () pour résoudre la vue et renvoyez la vue d'erreur correspondante . ModelAndView contient l'adresse de la page d'erreur et le contenu de la page.

Cliquez dans la méthode this.resolveErrorView () et passez à la classe AbstractErrorController, comme illustré dans la figure ci-dessous.

résoudreErrorView (), comme son nom l'indique: résolvez la vue des erreurs! La signification générale de cette méthode est d'obtenir tous les résolveurs de vue d'erreur (ErrorViewResolver), puis de traverser un par un, tant qu'un ModelAndView (correspondant à la vue d'erreur correspondante) est obtenu avec succès, il retournera immédiatement.

 

2. Faites attention au code près de la ligne horizontale dans la capture d'écran ci-dessus.

Qu'est-ce que ErrorViewResolver? Cliquez pour voir que c'est une interface. La méthode resolErrorView () appelée par la partie en pointillés est également une méthode de cette interface. Placez la souris près de "ErrorViewResolver", puis ctrl + H pour afficher son architecture. Trouvé que l'interface a une classe d'implémentation: DefaultErrorViewResolver

Est-ce familier? DefaultErrorViewResolver est un composant mentionné dans l'introduction précédente. Ensuite, nous entrons DefaultErrorViewResolver pour voir comment il résout la vue d'erreur (la capture d'écran ci-dessous).

Que signifient ces 3 méthodes? Voir les commentaires de code ci-dessous.

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, 
                                        Map<String, Object> model) {
        // 根据状态码精确匹配错误页面
	ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);

	if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
                // 如果无法找到准确状态码的错误页面,就模糊匹配:4xx(客户端错误),5xx(服务端错误)
		modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
	}

	return modelAndView;
}

private ModelAndView resolve(String viewName, Map<String, Object> model) {
	// 从上面的方法可以看出来,这里传入的viewName实际上就是状态码
	// 拼接视图名称,例如:error/404
	String errorViewName = "error/" + viewName;
	
	// 模板引擎可以解析这个页面地址就用模板引擎解析
	TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
			.getProvider(errorViewName, this.applicationContext);
								
	
	
	return provider != null ? 
        // 模板引擎可用的情况下返回到errorViewName指定的视图地址
                new ModelAndView(errorViewName, model) 
        // 否则就在静态资源文件夹下找errorViewName对应的页面,例如:error/404.html
                : this.resolveResource(errorViewName, model);
}

 

3. Suivez le train de la pensée et arrivez ici. En fait, nous pouvons clairement savoir comment personnaliser notre propre page d'erreur! Ici, je vais le mentionner dans le sens de la pensée, et je le résumerai plus tard.

De ce qui précède, nous savons que tant que nous créons un nouveau dossier d'erreur sous le dossier static ou templates sous ressources, puis que nous plaçons la page Html correspondant au code d'état (nommé d'après le code d'état, par exemple: 404.html) sous le dossier d'erreur. Ou mettez simplement 4xx.html, 5xx.html, de sorte que toutes les erreurs client utilisent la page 4xx.html, et les erreurs serveur utilisent la page 5xx.html.

Cependant, à partir de l'analyse de code ci-dessus, nous pouvons également savoir que si 404.html et 4xx.html existent en même temps, SpringBoot donnera la priorité à 404.html.

 4. Jusqu'à ce qui précède, nous savons comment modifier la page statique correspondant à l'état d'erreur. Mais que faire si j'ai besoin de récupérer le message d'erreur de la page? Par exemple, je souhaite récupérer l'horodatage, le code d'état, les informations d'exception, etc. de cette erreur. Comment retirer ces informations et les afficher sur notre propre page statique?

Cette information est-elle familière? Oui, lorsque le visiteur n'est pas un navigateur, les données Json renvoyées par SpringBoot incluent ces informations. Ensuite, sur la page d'erreur par défaut de SpringBoot, les informations associées sont également affichées. Ainsi, lorsque nous personnalisons nous-mêmes la page d'erreur, SpringBoot doit également nous fournir ces données que nous pouvons utiliser. Nous pouvons même ajouter des informations supplémentaires!

Concernant ce problème, où se trouve le point d'entrée du code source? En fait, il a également été mentionné lorsque BasicErrorController a été mentionné au début (voir la figure ci-dessous). Cette fois, nous prêtons attention à la méthode getErrorAttributes (), et vous pouvez deviner ce que cela signifie en regardant le nom. Clique dessus.

Accédez à la classe de base AbstractErrorController de BasicErrorController (voir la figure ci-dessous), puis cliquez pour entrer (getErrorAttributes () dans la partie soulignée de la figure ci-dessous).

Accédez à l'interface d'ErrorAttributes. Nous ctrl + H pour visualiser l'architecture de cette interface.

Effectivement, il a une classe d'implémentation: DefaultErrorAttributes. N'est-ce pas familier? C'est également l'un des 4 éléments clés mentionnés ci-dessus. Attention ici! ! ! Il existe deux DefaultErrorAttributes avec le même nom dans SpringBoot. Ce que nous voulons examiner se trouve sous le package org.springframework.boot.web.servlet.error. Ne fais pas d'erreur! ! !

 

5. Accédez à DefaultErrorAttributes et voyez comment il partage les informations d'erreur et comment partager les informations d'erreur.

En regardant cette classe, nous avons trouvé: Les informations suivantes sont stockées dans la carte:

horodatage: horodatage

status: code d'état

erreur: message d'erreur

exception: objet d'exception

message: message d'exception

erreurs: les erreurs de vérification des données JSR303 sont ici

trace: informations de pile

Ces informations peuvent en fait être récupérées sous forme de variables de la page via la balise thymeleaf. Les exemples sont les suivants:

Faites attention à un petit problème ici: la version de SpringBoot que j'utilise n'a aucun moyen de récupérer les informations d'exception par défaut. La raison peut être connue à partir du code source (je ne vais pas y entrer ici, je posterai une image comme point d'entrée, vous pouvez suivre le code source vous-même), afin de résoudre ce problème, il suffit d'ajouter ce qui suit configuration dans le fichier de configuration: server.error. include-exception = true

la raison:

Quatre, données d'erreur personnalisées

Jusqu'à présent, nous savons:

(1) Comment personnaliser votre propre page d'erreur

(2) Comment utiliser les informations d'erreur existantes dans la page d'erreur personnalisée

(3) Comprendre le déroulement du mécanisme de gestion des erreurs par défaut de SpringBoot

Je pense que vous avez une compréhension plus approfondie des catégories clés mentionnées dans "II". Donc, maintenant, je souhaite également personnaliser les données d'erreur. En d'autres termes, je souhaite partager les mauvaises informations que je souhaite. Ce message d'erreur est personnalisé par moi, je l'ai ajouté au domaine partagé, puis je l'ai utilisé dans ma page d'erreur!

Une fois qu'une erreur se produit, elle aboutit à la requête / error, qui sera traitée par BasicErrorController. Les données d'erreur qui peuvent être obtenues en réponse sont obtenues par getErrorAttributes (la méthode spécifiée par AbstractErrorController (ErrorController)). Si nous voulons ajouter des informations d'erreur personnalisées, nous pouvons écrire une classe d'implémentation de ErrorController [ou une sous-classe de AbstractErrorController], la placer dans le conteneur et remplacer complètement le composant DefaultErrorAttributes de SpringBoot.

code montrer comme ci-dessous:

package com.ysq.springbootweb.component;


import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import java.util.Map;

/**
 *
 * @author passerbyYSQ
 * @create 2020-07-29 16:16
 */
@Component // 给容器中加入我们自己的ErrorAttributes
public class MyErrorAttributes extends DefaultErrorAttributes {

    //  如果不复写,Exception又获取不到了
    public MyErrorAttributes() {
        super(true);
    }

    /**
     * 这里返回的Map,就是页面和Json数据能获取到的所有字段
     * @param webRequest
     * @param includeStackTrace
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        // 额外信息
        map.put("company", "ysq");

        return map;
    }

}

Ici, nous allons rencontrer deux problèmes:

(1) Si la méthode de construction du paramètre nul n'est pas écrasée, la version actuelle de SpringBoot n'obtiendra pas les informations d'exception. En ce qui concerne l'acquisition d'Exception, je l'ai mentionné plus tôt. Mais quelle est la situation maintenant?

À l'origine, SpringBoot utilisait DefaultErrorAttributes et DefaultErrorAttributes ne mettait pas les informations d'exception dans le domaine partagé par défaut. Ce que nous avons précédemment modifié via le fichier de configuration est de permettre à DefaultErrorAttributes de placer les informations d'exception dans le domaine partagé. Aujourd'hui, nous remplaçons complètement le composant DefaultErrorAttributes en écrivant une sous-classe de DefaultErrorAttributes et en la plaçant dans le conteneur. SpringBoot n'utilisera pas DefaultErrorAttributes, mais utilisera à la place complètement notre composant personnalisé MyErrorAttributes. Par conséquent, la configuration du fichier de configuration n'est pas valide. Par conséquent, nous pouvons directement remplacer la structure de paramètre null de la classe parente de MyErrorAttributes, permettant d'obtenir des informations d'exception.

Cette question est un épisode. La question suivante (2) est au centre.

(2) Écrivez MyErrorAttributes directement, remplacez la méthode parente getErrorAttributes et ajoutez des informations d'erreur personnalisées à la carte avant que la méthode ne renvoie le résultat. Cela conduit à un problème: quelle que soit l'erreur, le même message d'erreur sera ajouté. Je souhaite ajouter des informations spécifiques pour différentes erreurs.

Par exemple: une erreur 500 causée par une exception personnalisée, ajoutez ses informations uniques pour que la page d'erreur accède.

Comment résoudre cette demande? Voir le code ci-dessous

package com.ysq.springbootweb.controller;

import com.ysq.springbootweb.exception.UserNotExistException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

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

/**
 * @author passerbyYSQ
 * @create 2020-07-29 15:47
 */
@ControllerAdvice // 在SpringMVC要成为异常处理器,需要增加此注解
public class MyExceptionHandler {

    // 只要发生UserNotExistException异常,SpringMVC就调用该方法,并将异常对象对传递进来
    @ExceptionHandler(UserNotExistException.class) 
    public String handleException(Exception e, HttpServletRequest request) {

        Map<String, Object> map = new HashMap<>();

        // 需要传入我们自己的错误状态
        // 如果不传,就默认200(成功),就不会进入自己定制的错误页面的解析流程
        // 虽然有定制的页面效果了,但是我定制的Map数据不生效
        request.setAttribute("javax.servlet.error.status_code", 500);

        // 自定义的错误信息
        map.put("code", "user not exist");
        map.put("message", "用户不存在!!!!");
        // 将额外的信息添加到请求域中,以供MyErrorAttributes取出
        request.setAttribute("ext", map);

        // 转发到/error请求,让BasicErrorController处理
        return "forward:/error"; 
    }
}

En même temps, apportez les modifications suivantes à MyErrorAttributes:

package com.ysq.springbootweb.component;


import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import java.util.Map;

/**
 *
 * @author passerbyYSQ
 * @create 2020-07-29 16:16
 */
@Component // 给容器中加入我们自己的ErrorAttributes
public class MyErrorAttributes extends DefaultErrorAttributes {

    //  如果不复写,Exception又获取不到了
    public MyErrorAttributes() {
        super(true);
    }

    /**
     * 这里返回的Map,就是页面和Json数据能获取到的所有字段
     * @param webRequest
     * @param includeStackTrace
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        // 额外信息
        map.put("company", "ysq");

        // 针对UserNotExistException异常的错误信息
        Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
        if (ext != null) {
            map.put("ext", ext);
        }

        return map;
    }

}

Mon effet de page:

Voyez les amis ici, n'oubliez pas de les aimer et de les collectionner. Je vous remercie!

Je suppose que tu aimes

Origine blog.csdn.net/qq_43290318/article/details/107669722
conseillé
Classement