Quand devez-vous lancer une exception
Nous devons d’abord comprendre une question : quand devons-nous lever une exception ? Les exceptions sont conçues pour être pratiques à utiliser par les développeurs, mais pas pour être utilisées sans discernement. L'auteur a également demandé à de nombreux amis quand lancer des exceptions, et rares sont en effet ceux qui peuvent donner des réponses précises. En fait, ce problème est très simple. Si vous pensez que certains « problèmes » ne peuvent pas être résolus, vous pouvez lever une exception. Par exemple, si vous écrivez un service et que lorsque vous écrivez un certain morceau de code, vous constatez qu'un problème peut survenir, alors lancez une exception. Croyez-moi, c'est le meilleur moment pour lancer une exception.
Quel genre d'exception devrait être levée
Après avoir compris quand nous devons lever une exception, réfléchissons à une autre question. Lorsque nous levons réellement une exception, quel type d'exception devrions-nous choisir ? S'agit-il d'une exception vérifiée ou d'une exception non vérifiée (RuntimeException) ? Permettez-moi d'illustrer ce problème avec un exemple, en commençant par les exceptions vérifiées. Par exemple, il existe une telle logique métier qui doit lire certaines données d'un certain fichier. Cette opération de lecture peut ne pas être possible en raison d'autres problèmes tels que la suppression de fichiers. Si une erreur de lecture se produit en raison de l'acquisition, alors les données doivent être obtenues à partir de la base de données redis ou mysql. Reportez-vous au code suivant, getKey(Integer) est le programme d'entrée.
. chaîne publique getKey (clé entière) {
Valeur de chaîne ;
essayez {
InputStream inputStream = getFiles("/file/nofile");
//Ensuite, lisez la valeur de la clé dans le flux
valeur = ...;
} attraper (Exception e) {
//Si une exception est levée, elle sera extraite de MySQL ou Redis
valeur = ...;
}
}
public InputStream getFiles (chemin de chaîne) lève une exception {
Fichier fichier = nouveau fichier (chemin);
InputStream inputStream = null;
essayez {
inputStream = nouveau BufferedInputStream (nouveau FileInputStream (fichier));
} catch (FileNotFoundException e) {
throw new Exception("Erreur de lecture E/S",e.getCause());
}
retourner inputStream;
}
Ok, après avoir lu le code ci-dessus, vous avez peut-être quelques idées en tête. Il s'avère que les exceptions vérifiées peuvent contrôler la logique d'obligation. Oui, c'est vrai, vous pouvez vraiment contrôler la logique métier via des exceptions vérifiées, mais n'oubliez pas de ne pas les utiliser. comme ça. Nous devrions être raisonnables. Lancez des exceptions car le programme lui-même est le processus. La fonction des exceptions n'est qu'une excuse lorsque vous ne pouvez pas continuer. utilisé de cette manière, il utilise des exceptions. Le rôle étendu entraînera une complexité accrue du code, un couplage accru et une lisibilité réduite du code. Il ne faut donc pas recourir à de telles exceptions ? En fait, non, lorsqu’un tel besoin existe réellement, nous pouvons l’utiliser de cette manière, mais n’oubliez pas de ne pas vraiment le considérer comme un outil ou un moyen de contrôler le processus. Alors, quand une telle exception devrait-elle être levée ? Il convient de considérer que si l'appelant fait une erreur lors de l'appel, il doit être autorisé à gérer l'erreur. Ce n'est que lorsque ces exigences sont remplies que nous envisagerons d'utiliser des exceptions vérifiées.
Examinons ensuite les exceptions non vérifiées (RuntimeException). Nous voyons en fait beaucoup d'exceptions comme RuntimeException, telles que java.lang.NullPointerException/java.lang.IllegalArgumentException, etc. Alors, quand lancer ce genre d'exception ? Lorsque nous écrivons une certaine méthode, nous pouvons rencontrer accidentellement une erreur. Nous pensons que ce problème peut se produire pendant l'exécution, et théoriquement, s'il n'y a pas de problème, le programme s'exécutera normalement. RuntimeException est levée à ce moment. Par exemple, lorsqu'un chemin est passé, un objet File correspondant au chemin doit être renvoyé :
test de vide public() {
monTest.getFiles("");
}
fichier public getFiles (chemin de la chaîne) {
if(null == chemin || "".equals(chemin)){
throw new NullPointerException("Le chemin ne peut pas être vide!");
}
Fichier fichier = nouveau fichier (chemin);
retourner le fichier ;
}
L'exemple ci-dessus montre que si le chemin est vide lorsque l'appelant appelle getFiles(String), alors une exception de pointeur nul (qui est une sous-classe de RuntimeException) sera levée et l'appelant n'a pas besoin d'effectuer explicitement un essai... catch... opération pour un traitement forcé. Cela nécessite que l'appelant vérifie d'abord lorsqu'il appelle une telle méthode pour éviter RuntimeException comme suit :
. Quelle exception doit être utilisée
Grâce à la description et aux exemples ci-dessus, nous pouvons tirer une conclusion. La différence entre RuntimeException et l'exception vérifiée est la suivante : si l'appelant est obligé de gérer cette exception, utilisez l'exception vérifiée. sélectionnez une exception non cochée (RuntimeException). De manière générale, s'il n'y a pas d'exigences particulières, nous vous recommandons d'utiliser RuntimeException.
Introduction du scénario et sélection de la technologie
Description de l'architecture
Comme nous le savons, les projets traditionnels sont développés sur la base du framework MVC. Cet article utilise principalement la conception d'une interface de style reposant pour découvrir l'élégance de la gestion des exceptions.
Nous nous concentrons sur la couche API reposante (similaire à la couche contrôleur sur le Web) et la couche service, étudions comment les exceptions sont générées dans le service, puis comment la couche API capture et transforme les exceptions.
Les technologies utilisées sont : spring-boot, jpa (hibernate), mysql. Si vous n'êtes pas familier avec ces technologies, les lecteurs doivent lire eux-mêmes les documents pertinents.
Description du scénario commercial
Choisissez un scénario commercial relativement simple, en prenant comme exemple la gestion de l'adresse de livraison dans le commerce électronique. Lorsque les utilisateurs achètent des marchandises sur le terminal mobile, ils doivent gérer l'adresse de livraison dans le projet, certaines interfaces API sont fournies pour les terminaux mobiles. accès, tels que : ajouter une adresse de livraison, supprimer une adresse de livraison, modifier une adresse de livraison, définir une adresse de livraison par défaut, une requête de liste d'adresses de livraison, une requête d'adresse de livraison unique et d'autres interfaces.
Construire des contraintes
ok, il s'agit d'un scénario commercial très basique qui a été mis en place. Bien sûr, quel que soit le type d'opération de l'API, il contient quelques règles :
. Ajouter l'adresse de livraison :
Ingrédients :
Identifiant d'utilisateur
Réception des informations sur l'entité d'adresse
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
Les champs obligatoires pour l'adresse de livraison ne peuvent pas être vides
Si l'utilisateur n'a pas encore d'adresse de livraison, cette adresse de livraison sera définie comme adresse de livraison par défaut lors de sa création —
Supprimer l'adresse de livraison :
Ingrédients :
Identifiant d'utilisateur
Identifiant de l'adresse de livraison
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
L'adresse de livraison ne peut pas être vide, et l'adresse de livraison existe
Déterminez si cette adresse de livraison est l’adresse de livraison de l’utilisateur
Déterminez si cette adresse de livraison est l'adresse de livraison par défaut. Si c'est l'adresse de livraison par défaut, elle ne peut pas être supprimée
. Modifier l'adresse de livraison :
Ingrédients :
Identifiant d'utilisateur
Identifiant de l'adresse de livraison
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
L'adresse de livraison ne peut pas être vide, et l'adresse de livraison existe
Déterminez si cette adresse de livraison est l’adresse de livraison de l’utilisateur
Paramètre d'adresse par défaut :
Ingrédients :
Identifiant d'utilisateur
Identifiant de l'adresse de livraison
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
L'adresse de livraison ne peut pas être vide, et l'adresse de livraison existe
Déterminez si cette adresse de livraison est l’adresse de livraison de l’utilisateur
Requête de liste d'adresses de livraison :
Ingrédients :
Identifiant d'utilisateur
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
Demande d'adresse de livraison unique :
Ingrédients :
Identifiant d'utilisateur
Identifiant de l'adresse de livraison
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
L'adresse de livraison ne peut pas être vide, et l'adresse de livraison existe
Déterminez si cette adresse de livraison est l’adresse de livraison de l’utilisateur
Jugement de contrainte et sélection de technologie
Pour les contraintes et les listes de fonctions répertoriées ci-dessus, j'ai sélectionné plusieurs scénarios typiques de gestion des exceptions pour analyse : ajout d'une adresse de livraison, suppression d'une adresse de livraison et obtention d'une liste d'adresses de livraison.
Alors, de quelles réserves de connaissances devons-nous disposer ? Jetons un coup d’œil à la fonction d’adresse de livraison :
Lors de l'ajout d'une adresse de livraison, l'ID utilisateur et les informations de l'entité d'adresse de livraison doivent être vérifiées. Alors, comment sélectionner les outils pour un jugement non vide ? Le jugement traditionnel est le suivant :
/**
* Ajouter une adresse
* @param uid
* Adresse @param
* @retour
*/
adresse publique addAddress (uid entier, adresse d'adresse) {
si(null != uid){
//Processus..
}
renvoie null ;
}
Dans l'exemple ci-dessus, si vous jugez uniquement que l'uid est vide, tout ira bien. Si vous jugez ensuite si certains attributs nécessaires dans l'entité d'adresse sont vides, ce sera catastrophique lorsqu'il y a de nombreux champs.
Alors, comment devrions-nous porter ces jugements sur la saisie des paramètres ? Permettez-moi de vous présenter deux points de connaissance :
La classe Preconditions dans Guava implémente le jugement de nombreuses méthodes de saisie de paramètres
Spécification de validation JSR 303 (l'implémentation actuelle est relativement complète, hibernate-validator implémenté par hibernate)
Si ces deux technologies de recommandation sont utilisées, le jugement de la saisie des paramètres deviendra beaucoup plus simple. Il est recommandé à tout le monde d’utiliser ces technologies matures et ces boîtes à outils Jar, qui peuvent réduire une grande partie de la charge de travail inutile. Nous devons juste nous concentrer sur la logique métier. Plutôt que de retarder plus de temps en raison de ces jugements d'entrée.
Comment concevoir des exceptions Java avec élégance
présentation du domaine
Selon le scénario du projet, deux modèles de domaine sont requis, l'un est l'entité utilisateur et l'autre est l'entité adresse.
Le domaine d'adresse est le suivant :
@Entité
@Données
Adresse de classe publique {
@Id
@GeneratedValue
identifiant entier privé ;
province de chaîne privée ;//province
ville de chaîne privée ;//ville
Comté de String privé ; // district
private Boolean isDefault ;//Est-ce l'adresse par défaut
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="uid")
Utilisateur privé ;
}
Le domaine utilisateur est le suivant :
@Entité
@Données
Utilisateur de classe publique {
@Id
@GeneratedValue
identifiant entier privé ;
nom de chaîne privée ; // nom
@OneToMany(cascade= CascadeType.ALL,mappedBy="user",fetch = FetchType.LAZY)
ensemble privé
}
Ok, ce qui précède est une relation modèle, et la relation entre l'utilisateur et l'adresse de livraison est une relation 1-n. Le @Data ci-dessus utilise un outil appelé lombok, qui génère automatiquement les méthodes Setter et Getter, ce qui est très pratique à utiliser. Les lecteurs intéressés peuvent en apprendre davantage par eux-mêmes.
introduction au tao
Pour la couche de connexion de données, nous utilisons le framework spring-data-jpa, qui nous oblige à hériter uniquement de l'interface fournie par le framework et à nommer les méthodes selon la convention pour effectuer les opérations de base de données souhaitées.
Le fonctionnement de la base de données des utilisateurs est le suivant :
@Dépôt
interface publique IUserDao étend JpaRepository
Le fonctionnement de l'adresse de livraison est le suivant :
@Dépôt
interface publique IAddressDao étend JpaRepository
}Comme les lecteurs peuvent le constater, notre DAO n'a besoin que d'hériter de JpaRepository, et il nous a déjà aidé à effectuer des opérations de base CURD et d'autres. Si vous souhaitez en savoir plus sur le projet spring-data, veuillez vous référer à la documentation officielle de spring. recherche sur les anomalies.
Conception anormale du service
ok, nous avons enfin atteint notre objectif. Nous devons effectuer certaines opérations de service : ajouter une adresse de livraison, supprimer une adresse de livraison et obtenir la liste des adresses de livraison.
Regardez d’abord la définition de mon interface de service :
interface publique IAddressService {
/**
* Créer une adresse de livraison
* @param uid
* Adresse @param
* @retour
*/
Adresse createAddress (UID entier, adresse d'adresse);
/**
* Supprimer l'adresse de livraison
* @param uid
* @param aid
*/
void deleteAddress (UID entier, aide entière);
/**
* Interrogez toutes les adresses de livraison des utilisateurs
* @param uid
* @retour
*/
Liste
}
Concentrons-nous sur la mise en œuvre :
Ajouter une adresse de livraison
Tout d’abord, jetons un œil aux contraintes compilées précédemment :
Ingrédients :
Identifiant d'utilisateur
Réception des informations sur l'entité d'adresse
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
Les champs obligatoires pour l'adresse de livraison ne peuvent pas être vides
Si l'utilisateur n'a pas encore d'adresse de livraison, l'adresse de livraison sera définie comme adresse de livraison par défaut lors de sa création
Regardez d’abord l’implémentation du code suivant :
@Override
adresse publique createAddress (uid entier, adresse adresse) {
//============ Les contraintes suivantes sont ==============
//1. L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
Conditions préalables.checkNotNull(uid);
Utilisateur utilisateur = userDao.findOne(uid);
if(null == utilisateur){
throw new RuntimeException("Utilisateur actuel introuvable!");
}
//2. Les champs nécessaires de l'adresse de livraison ne peuvent pas être vides
BeanValidators.validateWithException(validateur, adresse);
//3. Si l'utilisateur n'a pas encore d'adresse de livraison, l'adresse de livraison sera définie comme adresse de livraison par défaut lors de sa création
if(ObjectUtils.isEmpty(user.getAddresses())){
adresse.setIsDefault(true);
}
//============ Ce qui suit est la logique métier normalement exécutée ==============
adresse.setUser(utilisateur);
Résultat de l'adresse = adresseDao.save(adresse);
résultat de retour ;
}
Parmi elles, les trois contraintes décrites ci-dessus ont été remplies. Lorsque les trois contraintes sont satisfaites, la logique métier normale peut être exécutée, sinon une exception sera levée (il est généralement recommandé de lancer une exception d'exécution - RuntimeException ici).
Présentation des technologies suivantes que j'ai utilisées ci-dessus :
Preconfigitions.checkNotNull(T t) est jugé à l'aide de com.google.common.base.Preconditions dans Guava. Étant donné que de nombreuses vérifications sont utilisées dans le service, il est recommandé de modifier les préconfigurations en importation statique :
. 1importer statique com.google.common.base.Preconditions.checkNotNull;
Bien sûr, les instructions du github de Guava nous recommandent également de l'utiliser de cette façon.
BeanValidators.validateWithException(validateur, adresse);
Cela se fait en utilisant la spécification jsr 303 implémentée par hibernate. Vous devez transmettre un validateur et une entité qui doit être vérifiée. Alors, comment obtenir le validateur, comme suit :
. @Configuration
classe publique BeanConfigs {
@Bean
public javax.validation.Validator getValidator(){
renvoyer un nouveau LocalValidatorFactoryBean();
}
}
Il obtiendra un objet Validator, puis nous pourrons l'injecter dans le service et l'utiliser :
@Autowired
validateur privé Validator ;
Alors, comment la classe BeanValidators est-elle implémentée ? En fait, l'implémentation est très simple. Jugez simplement l'annotation de jsr 303 et tout ira bien.
Alors, où sont écrites les annotations jsr 303 ? Bien sûr, c'est écrit dans la classe d'entité adresse :
@Entité
@Setter
@Getter
Adresse de classe publique {
@Id
@GeneratedValue
identifiant entier privé ;
@NotNull
province de chaîne privée ;//province
@NotNull
ville de chaîne privée ;//ville
@NotNull
Comté de String privé ; // district
private Boolean isDefault = false;//Est-ce l'adresse par défaut
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="uid")
Utilisateur privé ;
}
Écrivez les contraintes dont vous avez besoin pour porter des jugements. Si elles sont raisonnables, vous pouvez effectuer des opérations commerciales et exploiter la base de données.
La vérification de cette pièce est nécessaire. L'une des principales raisons est qu'une telle vérification peut éviter l'insertion de données sales. Si les lecteurs ont une expérience formelle en ligne, ils peuvent comprendre une telle chose. Toute erreur de code peut être tolérée et modifiée, mais si un problème de données sales survient, cela peut être un désastre dévastateur. Les problèmes du programme peuvent être corrigés, mais l'apparence de données sales peut ne pas être récupérable. C'est pourquoi vous devez déterminer les contraintes du service avant d'effectuer des opérations de logique métier.
Le jugement ici est un jugement de logique métier, qui est effectué d'un point de vue commercial. De plus, il peut y avoir différentes contraintes de conditions commerciales dans de nombreux scénarios, et vous devez le faire uniquement en fonction des exigences.
Un résumé des contraintes est le suivant :
Contraintes de jugement de base (jugements de base tels que valeur nulle)
Contraintes d'attribut d'entité (répondent aux jugements de base tels que jsr 303)
Contraintes de conditions métier (différentes contraintes métier proposées par les exigences)
Lorsque ces trois points sont satisfaits, vous pouvez passer à l'étape suivante
ok, cela introduit essentiellement comment porter un jugement de base. Revenons à la question de la conception des exceptions. Le code ci-dessus a clairement décrit comment juger raisonnablement une exception à l'emplacement approprié.
Le simple fait de lancer une RuntimeException compte-t-il comme lancer une exception avec élégance ? Bien sûr que non. En ce qui concerne les exceptions lancées dans les services, je pense qu'il existe à peu près deux méthodes pour lancer des exceptions :
Lève une exception avec le code d'état RumtimeException
Lance une RuntimeException du type spécifié
Par rapport à ces deux exceptions, la première exception signifie que toutes mes exceptions génèrent RuntimeException, mais elle doit avoir un code d'état. L'appelant peut demander quel type de service l'a lancé en fonction de l'exception du code d'état.
Le deuxième type d'exception fait référence à la personnalisation d'une erreur d'exception spécifiée pour toute exception levée dans le service, puis à la levée de l'exception.
D'une manière générale, si le système n'a pas d'autres exigences particulières, il est recommandé d'utiliser la deuxième méthode lors du développement et de la conception. Mais par exemple, les exceptions de jugement de base peuvent être entièrement exploitées à l'aide de la bibliothèque de classes fournie par guava. Les exceptions JSR 303 peuvent également être exploitées à l'aide de leurs propres classes de jugement d'exception encapsulées, car ces deux exceptions sont des jugements de base et il n'est pas nécessaire de spécifier des exceptions spéciales pour elles. Mais pour l'exception levée par le troisième jugement de contrainte de condition d'obligation, vous devez lever une exception du type spécifié.
Pour
1 lancez une nouvelle RuntimeException ("Utilisateur actuel introuvable !");
Définissez une classe d'exception spécifique pour juger de cette exception d'obligation :
la classe publique NotFindUserException étend RuntimeException {
public NotFindUserException() {
super("Cet utilisateur est introuvable");
}
public NotFindUserException (message de chaîne) {
super(message);
}
}
Ensuite, changez ceci en :
1lancez une nouvelle exception NotFindUserException("L'utilisateur actuel est introuvable!");
ou
1lancez une nouvelle NotFindUserException();
ok, grâce aux modifications ci-dessus apportées à la couche de service, les changements de code sont les suivants :
@Override
adresse publique createAddress (uid entier, adresse adresse) {
//============ Les contraintes suivantes sont ==============
//1. L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
checkNotNull(uid);
Utilisateur utilisateur = userDao.findOne(uid);
if(null == utilisateur){
throw new NotFindUserException("Utilisateur actuel introuvable!");
}
//2. Les champs nécessaires de l'adresse de livraison ne peuvent pas être vides
BeanValidators.validateWithException(validateur, adresse);
//3. Si l'utilisateur n'a pas encore d'adresse de livraison, l'adresse de livraison sera définie comme adresse de livraison par défaut lors de sa création
if(ObjectUtils.isEmpty(user.getAddresses())){
adresse.setIsDefault(true);
}
//============ Ce qui suit est la logique métier normalement exécutée ==============
adresse.setUser(utilisateur);
Résultat de l'adresse = adresseDao.save(adresse);
résultat de retour ;
}
Un tel service semble plus stable et plus compréhensible.
Supprimer l'adresse de livraison :
Ingrédients :
identifiant utilisateur
Identifiant de l'adresse de livraison
Contraintes :
L'ID utilisateur ne peut pas être vide et cet utilisateur existe
L'adresse de livraison ne peut pas être vide, et l'adresse de livraison existe
Déterminez si cette adresse de livraison est l’adresse de livraison de l’utilisateur
Déterminez si cette adresse de livraison est l'adresse de livraison par défaut. Si c'est l'adresse de livraison par défaut, elle ne peut pas être supprimée
. Cela revient à ajouter l'adresse de livraison ci-dessus, je n'entrerai donc pas dans les détails. La conception du service de suppression est la suivante : @Override
. public void deleteAddress (UID entier, aide entière) {
//============ Les contraintes suivantes sont ==============
//1. L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
checkNotNull(uid);
Utilisateur utilisateur = userDao.findOne(uid);
if(null == utilisateur){
lancer une nouvelle NotFindUserException();
}
//2. L'adresse de livraison ne peut pas être vide, et l'adresse de livraison existe
checkNotNull(aide);
Adresse adresse = adresseDao.findOne(aid);
if(null == adresse){
lancer une nouvelle NotFindAddressException();
}
//3. Déterminer si cette adresse de livraison est l'adresse de livraison de l'utilisateur
if(!address.getUser().equals(user)){
lancer une nouvelle NotMatchUserAddressException();
}
//4. Déterminez si cette adresse de livraison est l'adresse de livraison par défaut. Si c'est l'adresse de livraison par défaut, elle ne peut pas être supprimée
. si(adresse.getIsDefault()){
lancer une nouvelle exception DefaultAddressNotDeleteException();
}
//============ Ce qui suit est la logique métier normalement exécutée ==============
adresseDao.delete(adresse);
}
Quatre classes d'exceptions associées sont conçues : NotFindUserException, NotFindAddressException, NotMatchUserAddressException, DefaultAddressNotDeleteException. Différentes exceptions sont levées en fonction des différentes exigences métier.
Obtenez la liste des adresses de livraison :
Ingrédients :
identifiant utilisateur
Contraintes :
L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
Le code est le suivant : @Override
liste publique
//============ Les contraintes suivantes sont ==============
//1. L'identifiant de l'utilisateur ne peut pas être vide, et cet utilisateur existe
checkNotNull(uid);
Utilisateur utilisateur = userDao.findOne(uid);
if(null == utilisateur){
lancer une nouvelle NotFindUserException();
}
//============ Ce qui suit est la logique métier normalement exécutée ==============
Résultat utilisateur = userDao.findOne(uid);
return result.getAddresses();
}
Conception anormale de l'API
Il existe en gros deux méthodes de lancer :
Lève une exception avec le code d'état RumtimeException
Lance une RuntimeException du type spécifié
Cela a été mentionné lors de la conception des exceptions de la couche de service. Grâce à l'introduction de la couche de service, nous avons choisi la deuxième méthode de lancement lorsque la couche de service génère des exceptions. La différence est que lors du lancement d'exceptions dans la couche API, nous devons utiliser Il existe deux manières. pour lancer : spécifiez le type d'exception API, spécifiez le code d'état correspondant, puis lancez l'exception. Le cœur de cette conception d'exception est de permettre aux utilisateurs qui appellent l'API de comprendre plus clairement l'exception. nous devons également créer un tableau correspondant pour afficher les informations détaillées de l'exception correspondant au code d'état et les éventuels problèmes pouvant survenir avec l'exception à l'utilisateur afin de faciliter la requête de l'utilisateur. (Comme la documentation de l'API fournie par github, la documentation de l'API fournie par WeChat, etc.), il existe un autre avantage : si l'utilisateur a besoin de personnaliser le message d'invite, l'invite peut être modifiée en fonction du code d'état renvoyé.
Contraintes de validation de l'API
Tout d'abord, pour la conception de l'API, il doit y avoir un objet dto. Cet objet est responsable de la communication et du transfert des données avec l'appelant, puis le domaine dto-> est transmis au service pour fonctionnement. On y a prêté attention. Deuxièmement, Point, en plus du service mentionné nécessitant un jugement de base (jugement nul) et une vérification jsr 303, la couche API doit également effectuer la vérification associée. Si la vérification échoue, elle sera directement renvoyée à l'appelant. informez l'appel qu'il a échoué et ne devrait pas le faire. Si vous accédez au service avec des données illégales, les lecteurs peuvent être un peu confus. Si le service n'a pas été vérifié, pourquoi la couche API doit-elle encore être vérifiée ? Un concept est conçu ici : la loi de Murphy en programmation. Si la vérification des données au niveau de la couche API est négligée, des données illégales peuvent être transférées vers la couche de service, puis des données sales peuvent être enregistrées dans la base de données.
Le cœur d’une programmation rigoureuse est donc le suivant : ne croyez jamais que les données reçues sont légales.
Conception anormale de l'API
Lors de la conception d'exceptions de couche API, comme nous l'avons dit ci-dessus, vous devez fournir des codes d'erreur et des messages d'erreur. Vous pouvez ensuite le concevoir comme ceci pour fournir une exception de super classe API générale. D'autres exceptions API différentes héritent de cette super classe :
. la classe publique ApiException étend RuntimeException {
Code d'erreur long protégé ;
données d'objet protégées ;
public ApiException (Code d'erreur long, message de chaîne, données d'objet, e jetable){
super(message,e);
this.errorCode = errorCode ;
this.data = data ;
}
public ApiException (Code d'erreur long, message de chaîne, données d'objet){
this(code d'erreur, message, données, null);
}
public ApiException (Code d'erreur long, message de chaîne){
this(code d'erreur, message, nul, nul);
}
public ApiException (String message, Throwable e){
this(nul, message, nul, e);
}
public ApiException(){
}
public ApiException (Throwable e){
super(e);
}
public Long getErrorCode() {
renvoyer le code d'erreur ;
}
public void setErrorCode (Code d'erreur long) {
this.errorCode = errorCode;
}
Objet public getData() {
renvoyer des données ;
}
public void setData (données d'objet) {
this.data = data;
}
}
Description de l'api:ApiDefaultAddressNotDeleteException,ApiNotFindAddressException,ApiNotFindUserException,ApiNotMatchUserAddressException。
以默认地址不能删除为例 :
la classe publique ApiDefaultAddressNotDeleteException étend ApiException {
public ApiDefaultAddressNotDeleteException (message de chaîne) {
super(AddressErrorCode.DefaultAddressNotDeleteErrorCode, message, null);
}
}
AddressErrorCode.DefaultAddressNotDeleteErrorCode就是需要提供给调用者的错误码。错误码类如下:
classe abstraite publique AddressErrorCode {
public static final Long DefaultAddressNotDeleteErrorCode = 10001L;//默认地址不能删除
public static final Long NotFindAddressErrorCode = 10002L;//找不到此收货地址
public static final Long NotFindUserErrorCode = 10003L;//找不到此用户
public static final Long NotMatchUserAddressErrorCode = 10004L;//用户与收货地址不匹配
}
ok, il s'agit d'une application api, d'un code d'erreur d'adresse.合理的做法是把他放到配置文件中进行管理。
api处理异常
api service service做成一个转发的功能就好(接口参数,传递给service参数, (((((((((((((
此处仅以添加地址为例:
@Autowired
adresseService IAddressService privée ;
/**
*Ajouter une adresse de livraison
* @param adresseDTO
* @retour
*/
@RequestMapping(method = RequestMethod.POST)
public AddressDTO add (@Valid @RequestBody AddressDTO adresseDTO){
Adresse adresse = nouvelle adresse ();
BeanUtils.copyProperties(adresseDTO,adresse);
Résultat de l'adresse ;
essayez {
résultat = adresseService.createAddress(addressDTO.getUid(), adresse);
}attraper (NotFindUserException e){
lancez une nouvelle ApiNotFindUserException("找不到该用户");
}catch (Exception e){//未知错误
lancer une nouvelle ApiException(e);
}
AddressDTO resultDTO = new AddressDTO();
BeanUtils.copyProperties(result,resultDTO);
resultDTO.setUid(result.getUser().getId());
retourner le résultatDTO ;
}
Le service api, le service api,这是常用的一种异常转化方式。相似删除收货地址和获取收货地址也类似这样处理,在此,不在赘述。
api异常转化
Les services d'api et de service sont également disponibles.了异常处理呢? 答案是否定的,当抛出api异常后,我们需要把api异常返回的数据(json or xml)让用户看懂,那么需要把api异常转化成dto对象(ErrorDTO),看如下代码:
@ControllerAdvice(annotations = RestController.class)
classe ApiExceptionHandlerAdvice {
/**
* Gérer les exceptions levées par les gestionnaires.
*/
@ExceptionHandler (valeur = Exception.class)
@ResponseBody
public ResponseEntity
ErreurDTO erreurDTO = new ErreurDTO();
if(exception instanceof ApiException){//api异常
ApiException apiException = (ApiException)exception ;
errorDTO.setErrorCode(apiException.getErrorCode());
}elsa{//未知异常
errorDTO.setErrorCode(0L);
}
errorDTO.setTip(exception.getMessage());
ResponseEntity
return réponseEntity ;
}
@Setter
@Getter
classe ErreurDTO{
code d'erreur long privé ;
Astuce de chaîne privée ;
}
}
ok, il s'agit d'un api qui s'en occupe.殊的切面处理。
Lorsqu'une exception se produit lors de l'appel de l'interface API, l'utilisateur peut également recevoir le format de données normal. Par exemple, lorsqu'il n'y a pas d'utilisateur (l'uid est 2), mais l'adresse de livraison est ajoutée pour cet utilisateur, postman (le plugin Google est utilisé). pour simuler des requêtes http) Données :
{
"code d'erreur": 10003,
"tip": "L'utilisateur est introuvable"
}
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!