Pas beaucoup de commérages, continuez à optimiser le cadre de réponse global unifié de l'API Restful pour rendre l'interface générale du projet évolutive.
Si vous n'avez pas lu les articles précédents, veuillez lire les articles précédents
SpringBoot définit un cadre de réponse API Restful global unifié élégant
SpringBoot définit un cadre de réponse d'API Restful global unifié élégant 2
SpringBoot définit un cadre de réponse API Restful global unifié élégant trois
SpringBoot définit un cadre de réponse API Restful global unifié élégant quatre
SpringBoot définit un cadre de réponse API Restful global unifié élégant cinq
Voici une discussion sur la dernière version et quelques problèmes qui doivent être corrigés
@PostMapping("/add/UserApiCombo")
public R addApiCombo(@RequestBody @Validated UserApplyApiComboDto userApplyApiComboDto) {
userApiComboService.addApiCombo(userApplyApiComboDto);
return R.success();
}
Regardons ce code, ce qui ne va pas. Nous avons renvoyé un ensemble de résultats encapsulé unifié R
, mais il controller
n'est pas convivial d'écrire tout ce qui suit.
- Le contenu renvoyé n'est pas assez clair
- Toute
controller
écriture de cette manière augmente la charge de travail de duplication
On peut optimiser comme ceci :
Spirng fournit ResponseBodyAdvice
une interface qui prend en charge le traitement du résultat de retour de l'interface avant que le convertisseur de message n'effectue la conversion, et combinée avec @ControllerAdvice
des annotations, les fonctions ci-dessus peuvent être facilement prises en charge
package cn.soboys.springbootrestfulapi.common.handler;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.soboys.springbootrestfulapi.common.error.ErrorDetail;
import cn.soboys.springbootrestfulapi.common.resp.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/6/12 12:17 下午
* @webSite https://github.com/coder-amiao
* @Slf4j
* @ControllerAdvice
*/
@Slf4j
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
/**
* supports方法: 判断是否要执行beforeBodyWrite方法,
* true为执行,false不执行.
* 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
/**
* beforeBodyWrite方法: 对response方法进行具体操作处理
* 实际返回结果业务包装处理
*
* @param body
* @param returnType
* @param selectedContentType
* @param selectedConverterType
* @param request
* @param response
* @return
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof R) {
return body;
} else if (body == null) {
return R.success();
} else if (body instanceof ErrorDetail) {
return body;
} else if (body instanceof String) {
return body;
} else {
return R.success().data(body);
}
}
}
Dans le controller
retour réel, nous pouvons directement retourner le contenu des données
@GetMapping("/home")
public Student home() {
Student s = new Student();
s.setUserName("Tom");
s.setAge(22);
List hobby = new ArrayList();
hobby.add("抽烟");
hobby.add("喝酒");
hobby.add("烫头");
s.setHobby(hobby);
s.setBalance(2229891.0892);
s.setIdCard("420222199811207237");
return s;
}
La logique de jugement des erreurs métier dans notre version actuelle n'est pas très conviviale et doit être optimisée. Ici, nous pouvons encapsuler nos propres exceptions métier et
utiliser Assert (assertion) pour encapsuler les exceptions afin de rendre le code plus élégant
Conformément au principe de retour de priorité d'erreur
Normalement, notre code d'exception métier est écrit comme ceci
// 另一种写法
Order order = orderDao.selectById(orderId);
if (order == null) {
throw new IllegalArgumentException("订单不存在。");
}
Après avoir utilisé l'optimisation d'assertion
Order order = orderDao.selectById(orderId);
Assert.notNull(order, "订单不存在。");
En comparant les deux méthodes, il est évident que la première méthode est plus élégante, tandis que la seconde méthode est un bloc de code if {…} relativement laid. Alors , qu'y a-t-il derrière le magique Assert.notNull() ?
Voici le code que nous devons optimiser
En fait, de nombreux frameworks ont des outils Assert , y compris JAVA JDK. SpringBoot, spring a également son propre Assert
, mais il n'est pas conforme à notre propre logique métier de levée d'exceptions. Ici, nous pouvons personnaliser notre propre outil Assert
Jetons un coup d'œil à un code source
public abstract class Assert {
public Assert() {
}
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
}
Comme vous pouvez le voir, Assert nous aide en fait à encapsuler if {…} , n'est-ce pas incroyable ? Bien que simple, il est indéniable que l'expérience de codage a été améliorée d'au moins un cran.
Nous pouvons donc imiter Assert et écrire une classe d'assertion personnalisée, mais les exceptions levées après l'échec de l'assertion ne sont pas des exceptions intégrées telles que IllegalArgumentException , mais des exceptions définies par nous-mêmes.
- définir l'exception publique
package cn.soboys.springbootrestfulapi.common.exception;
import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
import lombok.Data;
/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/6/12 10:32 下午
* @webSite https://github.com/coder-amiao
*/
@Data
public class BaseException extends RuntimeException {
/**
* 返回码
*/
protected ResultCode resultCode;
/**
* 异常消息参数
*/
protected Object[] args;
public BaseException(ResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public BaseException(String code, String msg) {
super(msg);
this.resultCode = new ResultCode() {
@Override
public String getCode() {
return code;
}
@Override
public String getMessage() {
return msg;
}
@Override
public boolean getSuccess() {
return false;
}
;
};
}
public BaseException(ResultCode resultCode, Object[] args, String message) {
super(message);
this.resultCode = resultCode;
this.args = args;
}
public BaseException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
super(message, cause);
this.resultCode = resultCode;
this.args = args;
}
}
- Toutes les autres exceptions héritent de l'exception commune
package cn.soboys.springbootrestfulapi.common.exception;
import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/4/29 00:15
* @webSite https://github.com/coder-amiao
* 通用业务异常封装
*/
public class BusinessException extends BaseException {
public BusinessException(ResultCode resultCode, Object[] args, String message) {
super(resultCode, args, message);
}
public BusinessException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
super(resultCode, args, message, cause);
}
}
- Encapsulation de classe d'exception métier d'assertion
public interface Assert {
/**
* 创建异常
* @param args
* @return
*/
BaseException newException(Object... args);
/**
* 创建异常
* @param t
* @param args
* @return
*/
BaseException newException(Throwable t, Object... args);
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
*
* @param obj 待判断对象
*/
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
}
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
* <p>异常信息<code>message</code>支持传递参数方式,避免在判断之前进行字符串拼接操作
*
* @param obj 待判断对象
* @param args message占位符对应的参数列表
*/
default void assertNotNull(Object obj, Object... args) {
if (obj == null) {
throw newException(args);
}
}
}
Utilisation spécifique
/**
* 异常返回模拟
*
* @return
*/
@GetMapping("/exception")
public Student exception() {
Student s = null;
BusinessErrorCode.Sign_Error.assertNotNull(s,"secret秘钥不正确");
return s;
}
En entreprise, nous pouvons lancer directement des exceptions d'énumération de cette manière. Cela rend le code beaucoup plus simple et plus propre
Le proxy a été mis à jour pour le projet d'échafaudage du référentiel github
Faites attention au compte officiel, le programmeur continuera à produire du contenu de haute qualité à trois heures , dans l'espoir de vous apporter de l'inspiration et de l'aide