当前位置:网站首页>Development specification: interface unified return value format [resend]
Development specification: interface unified return value format [resend]
2022-07-05 23:09:00 【Bug trendsetter】
boolean success ; The success of .
T data ; Specific return value upon success , On failure: null .
Integer code ; Return on success 0 , Return specific error code in case of failure .
String message ; Return on success null , Return specific error message on failure .
2.2 Define error code In order to be compatible with various types of error codes , It can be solved by declaring interfaces , The interface is implemented by the specific business error code class .① First, in the demo-common Layer of com.example.demo.common Add... To the package error Catalog and create new ServiceErrors Error code interface class .package com.example.demo.common.error;
public interface ServiceErrors {
/**
* Get error code
*
* @return Integer
*/
Integer getCode();
/**
* Get error messages
*
* @return String
*/
String getMessage();
}
② Secondly, define a business error code enumeration class to implement the above interface class . package com.example.demo.common.error;
public enum DemoErrors implements ServiceErrors {
/**
* Error code
*/
SYSTEM_ERROR(10000, " System error "),
PARAM_ERROR(10001, " Parameter error "),
;
private Integer code;
private String message;
DemoErrors(Integer code, String message) {
this.code = code;
this.message = message;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
2.3 Definition Result Return to the packaging class Continue to demo-common Layer of com.example.demo.common Add... To the package entity Catalog and create new Result Return to the packaging class . It provides wrapSuccessfulResult And wrapErrorResult Method is used to return when an interface call succeeds or fails . package com.example.demo.common.entity;
import com.example.demo.common.error.ServiceErrors;
import java.io.Serializable;
public class Result<T> implements Serializable {
private T data;
private boolean success;
private Integer code;
private String message;
public Result() {
}
public static <T> Result<T> wrapSuccessfulResult(T data) {
Result<T> result = new Result<T>();
result.data = data;
result.success = true;
result.code = 0;
return result;
}
public static <T> Result<T> wrapSuccessfulResult(String message, T data) {
Result<T> result = new Result<T>();
result.data = data;
result.success = true;
result.code = 0;
result.message = message;
return result;
}
public static <T> Result<T> wrapErrorResult(ServiceErrors error) {
Result<T> result = new Result<T>();
result.success = false;
result.code = error.getCode();
result.message = error.getMessage();
return result;
}
public static <T> Result<T> wrapErrorResult(ServiceErrors error, Object... extendMsg) {
Result<T> result = new Result<T>();
result.success = false;
result.code = error.getCode();
result.message = String.format(error.getMessage(), extendMsg);
return result;
}
public static <T> Result<T> wrapErrorResult(Integer code, String message) {
Result<T> result = new Result<T>();
result.success = false;
result.code = code;
result.message = message;
return result;
}
public T getData() {
return this.data;
}
public Result<T> setData(T data) {
this.data = data;
return this;
}
public boolean isSuccess() {
return this.success;
}
public Result<T> setSuccess(boolean success) {
this.success = success;
return this;
}
public Integer getCode() {
return this.code;
}
public Result<T> setCode(Integer code) {
this.code = code;
return this;
}
public String getMessage() {
return this.message;
}
public Result<T> setMessage(String message) {
this.message = message;
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append("success=");
sb.append(this.success);
sb.append(",");
sb.append("code=");
sb.append(this.code);
sb.append(",");
sb.append("message=");
sb.append(this.message);
sb.append(",");
sb.append("data=");
sb.append(this.data);
sb.append("}");
return sb.toString();
}
}
2.4 Define business exception classes stay demo-biz Layer of com.example.demo.biz Add... To the package exception Catalog and create new BizException Exception class . package com.example.demo.biz.exception;
import com.example.demo.common.error.ServiceErrors;
public class BizException extends RuntimeException {
private final Integer code;
public BizException(ServiceErrors errors) {
super(errors.getMessage());
this.code = errors.getCode();
}
public BizException(Integer code, String message) {
super(message);
this.code = code;
}
public Integer getCode() {
return this.code;
}
}
2.5 Define exception handling facets After the preparatory work is done , Then there's the real unified format processing . Whether it's HTTP Interface still RPC Interface , When dealing with business logic , By throwing business exceptions , Again by Spring AOP Tangent snap and encapsulate return value , So as to achieve the goal of unifying the return value format of the external interface .① First, in the demo-web Layer of pom Introduce in the file Spring AOP The dependency package of . The package has been integrated in Spring Boot In the parent project provided , Just introduce it here . <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
② Secondly, in demo-web Layer of com.example.demo.web Add... To the package aspect Catalog and create new DubboServiceAspect Section class . adopt 「 Interceptor 」 And 「 Reflection 」 The implementation encapsulates the business exception as Result return . package com.example.demo.web.aspect;
import com.example.demo.biz.exception.BizException;
import com.example.demo.common.entity.Result;
import com.example.demo.common.error.DemoErrors;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
@Slf4j
@Component
public class DubboServiceAspect implements MethodInterceptor {
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
try {
return methodInvocation.proceed();
} catch (BizException e) {
log.error("BizException", e);
return exceptionProcessor(methodInvocation, e);
} catch (Exception e) {
log.error("Exception:", e);
return exceptionProcessor(methodInvocation, e);
}
}
private Object exceptionProcessor(MethodInvocation methodInvocation, Exception e) {
Object[] args = methodInvocation.getArguments();
Method method = methodInvocation.getMethod();
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
log.error("dubbo service [method=" + methodName + "] params=" + Arrays.toString(args) + " abnormal :", e);
Class<?> clazz = method.getReturnType();
if (clazz.equals(Result.class)) {
Result result = new Result();
result.setSuccess(false);
if (e instanceof BizException) {
result.setCode(((BizException) e).getCode());
result.setMessage(e.getMessage());
} else {
result.setCode(DemoErrors.SYSTEM_ERROR.getCode());
result.setMessage(DemoErrors.SYSTEM_ERROR.getMessage());
}
return result;
}
return null;
}
}
③ Define the processing class and then pass Spring XML The formal definition of facets , stay demo-web Layer of resources Create a new spring-aop.xml file , Define in it Dubbo Section of the interface . <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="dubboRemoteServiceAspect"
expression="execution(* com.example.demo.remote.service.*.*(..))"/>
<aop:advisor advice-ref="dubboServiceAspect" pointcut-ref="remoteServiceAspect"/>
</aop:config>
</beans>
④ Continue to demo-web Layer of resources Directory , New again application-context.xml Document management all Spring XML The configuration file , Now let's import spring-aop.xml file . <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-aop.xml"/>
</beans>
⑤ Last in DemoWebApplication Pass through in the entry class @ImportResource Annotation import Spring Of XML The configuration file . @ImportResource({"classpath:application-context.xml"})
At this time, the aspect handling the exception has been configured , Next, we modify the definition of RpcDemoService.test Methods to test whether the section is effective . 2.6 Section test ① First of all, will RpcDemoService.test The return result of the method is Result packing . package com.example.demo.remote.service;
import com.example.demo.common.entity.Result;
import com.example.demo.remote.model.param.DemoParam;
import com.example.demo.remote.model.result.DemoDTO;
public interface RpcDemoService {
/**
* Dubbo The interface test
*
* @param param DemoParam
* @return DemoDTO
*/
Result<DemoDTO> test(DemoParam param);
}
package com.example.demo.biz.service.impl.remote;
import com.alibaba.dubbo.config.annotation.Service;
import com.example.demo.biz.service.DemoService;
import com.example.demo.common.entity.Result;
import com.example.demo.remote.model.param.DemoParam;
import com.example.demo.remote.model.result.DemoDTO;
import com.example.demo.remote.service.RpcDemoService;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class RpcDemoServiceImpl implements RpcDemoService {
@Autowired
private DemoService demoService;
@Override
public Result<DemoDTO> test(DemoParam param) {
DemoDTO demo = new DemoDTO();
demo.setStr(demoService.test(param.getId()));
return Result.wrapSuccessfulResult(demo);
}
}
② Revise DemoService.test The internal logic of the method , After querying the database, first judge whether there is data , If not, throw a business exception . package com.example.demo.biz.service.impl;
import com.example.demo.biz.exception.BizException;
import com.example.demo.biz.service.DemoService;
import com.example.demo.common.error.DemoErrors;
import com.example.demo.dao.entity.UserDO;
import com.example.demo.dao.mapper.business.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.Objects;
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private UserMapper userMapper;
@Override
public String test(Integer id) {
Assert.notNull(id, "id Can't be empty ");
UserDO user = userMapper.selectById(id);
if (Objects.isNull(user)) {
throw new BizException(DemoErrors.USER_IS_NOT_EXIST);
}
return user.toString();
}
}
③ then cd To demo-remote Catalog , perform mvn deploy Order repacking . At this point, the adjustment of the service provider is over , Next through the test project to see the effect .④ Come to the test project , In adjustment TestController.test Method , increase id The ginseng . package com.yibao.dawn.web.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.example.demo.common.entity.Result;
import com.example.demo.remote.model.param.DemoParam;
import com.example.demo.remote.model.result.DemoDTO;
import com.example.demo.remote.service.RpcDemoService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("test")
public class TestController {
@Reference(version = "1.0.0.dev")
private RpcDemoService rpcDemoService;
@GetMapping("dubbo")
public Result<DemoDTO> test(@RequestParam("id") Integer id) {
DemoParam param = new DemoParam();
param.setId(id);
return rpcDemoService.test(param);
}
}
⑤ The test is transmitting parameters id = 1 And id = 2 Under the circumstances , The results are as follows : Because there are only id = 1 A piece of data , When you pass on the ginseng id = 2 It triggers DemoErrors.USER_IS_NOT_EXIST The business is abnormal . 3、 ... and 、HTTP Interface uniform return value format 3.1 Define the faceting class package com.example.demo.web.aspect;
import com.example.demo.biz.exception.BizException;
import com.example.demo.common.entity.Result;
import com.example.demo.common.error.DemoErrors;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class HttpServiceAspect implements MethodInterceptor {
@Override
public Result invoke(final MethodInvocation methodInvocation) throws Throwable {
Result result = new Result();
try {
String methodName = methodInvocation.getMethod().getName();
if (log.isDebugEnabled()) {
log.debug("starting business logic processing.... " + methodName);
}
result = (Result) methodInvocation.proceed();
if (log.isDebugEnabled()) {
log.debug("finished business logic processing...." + methodName);
}
} catch (BizException e) {
result.setSuccess(false);
result.setCode(e.getCode());
result.setMessage(e.getMessage());
} catch (IllegalArgumentException e) {
result.setSuccess(false);
result.setCode(DemoErrors.PARAM_ERROR.getCode());
result.setMessage(e.getMessage());
} catch (RuntimeException e) {
log.error(" System error ", e);
result.setSuccess(false);
result.setCode(DemoErrors.SYSTEM_ERROR.getCode());
result.setMessage(DemoErrors.SYSTEM_ERROR.getMessage());
}
return result;
}
}
3.2 Define facets stay spring-aop.xml Add a section definition to the file . <aop:config>
<aop:pointcut id="resultControllerAspect"
expression="@within(org.springframework.web.bind.annotation.RestController)
and execution(com.example.demo.common.entity.Result *.*(..))"/>
<aop:advisor advice-ref="httpServiceAspect" pointcut-ref="resultControllerAspect"/>
</aop:config>
Four 、 Conclusion So far, the method of unified return value format of the interface is introduced , If there are more internal projects in the company , You can extract some common components as a project and make them into two-party packages for other projects to rely on , Keep the internal projects unified . notes : The relevant code has been synchronized to GitHub Symon Lin
https://symonlin.github.io/2019/03/18/specification-1
Share & Looking at
边栏推荐
猜你喜欢
Fix the memory structure of JVM in one article
Week 17 homework
MoCo: Momentum Contrast for Unsupervised Visual Representation Learning
一文搞定class的微观结构和指令
Selenium+Pytest自动化测试框架实战
Yiwen gets rid of the garbage collector
一文搞定JVM的内存结构
Thoroughly understand JVM class loading subsystem
关于MySQL的30条优化技巧,超实用
CorelDRAW plug-in -- GMS plug-in development -- new project -- macro recording -- VBA editing -- debugging skills -- CDR plug-in (2)
随机推荐
Realize reverse proxy client IP transparent transmission
Function default parameters, function placeholder parameters, function overloading and precautions
Methods modified by static
3 find the greatest common divisor and the least common multiple
Use of shell:for loop
Nacos 的安装与服务的注册
Codeforces Global Round 19
Global and Chinese markets for reciprocating seal compressors 2022-2028: Research Report on technology, participants, trends, market size and share
LeetCode145. Post order traversal of binary tree (three methods of recursion and iteration)
Multi sensor fusion of imu/ optical mouse / wheel encoder (nonlinear Kalman filter)
[digital signal denoising] improved wavelet modulus maxima digital signal denoising based on MATLAB [including Matlab source code 1710]
Use the rewrite rule to rewrite all accesses to the a domain name to the B domain name
Alibaba Tianchi SQL training camp task4 learning notes
First, redis summarizes the installation types
Leecode learning notes
Global and Chinese markets of tantalum heat exchangers 2022-2028: Research Report on technology, participants, trends, market size and share
Element operation and element waiting in Web Automation
Judge whether the binary tree is a complete binary tree
Data type, variable declaration, global variable and i/o mapping of PLC programming basis (CoDeSys)
Hcip day 12 (BGP black hole, anti ring, configuration)