网站建设知识论文,wordpress添加项目,2018钓鱼网站建设,旧手机 搭建wordpress1、接口的响应要明确表示接口的处理结果
为了将接口设计得更合理#xff0c;我们需要考虑如下两个原则#xff1a; 对外隐藏内部实现。即服务A调用服务B#xff0c;如果服务B异常#xff0c;但是我们不要直接把服务B的状态码、错误描述直接暴露给用户#xff1b; 设计接…1、接口的响应要明确表示接口的处理结果
为了将接口设计得更合理我们需要考虑如下两个原则 对外隐藏内部实现。即服务A调用服务B如果服务B异常但是我们不要直接把服务B的状态码、错误描述直接暴露给用户 设计接口结构时明确每个字段的含义以及客户端的处理方式。
比如下面这个是我们设计的接口的响应
Data
public class APIResponseT {private boolean success;private T data;private int code;private String message;
}接口的设计逻辑 如果出现非 200 的 HTTP 响应状态码就代表请求没有到服务可能是网络出问题、网络超时或者网络配置的问题。这时肯定无法拿到服务端的响应体客户端可以给予友好提示比如让用户重试不需要继续解析响应结构体。 如果 HTTP 响应码是 200解析响应体查看 success为 false 代表下单请求处理失败可能是因为服务参数验证错误也可能是因为服务操作失败。这时根据服务定义的错误码表和 code做不同处理。比如友好提示或是让用户重新填写相关信息其中友好提示的文字内容可以从 message 中获取。 success 为 true 的情况下才需要继续解析响应体中的 data 结构体。data 结构体代表了业务数据。
1.1、通过ResponseBodyAdvice完成自动包装响应体
为了代码会更简洁我们的业务逻辑中可以通过ResponseBodyAdvice完成响应体的包装。
RestControllerAdvice
Slf4j
public class APIResponseAdvice implements ResponseBodyAdviceObject {//自动处理APIException包装为APIResponseExceptionHandler(APIException.class)public APIResponse handleApiException(HttpServletRequest request, APIException ex) {log.error(process url {} failed, request.getRequestURL().toString(), ex);APIResponse apiResponse new APIResponse();apiResponse.setSuccess(false);apiResponse.setCode(ex.getErrorCode());apiResponse.setMessage(ex.getErrorMessage());return apiResponse;}//仅当方法或类没有标记NoAPIResponse才自动包装Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return returnType.getParameterType() ! APIResponse.class AnnotationUtils.findAnnotation(returnType.getMethod(), NoAPIResponse.class) null AnnotationUtils.findAnnotation(returnType.getDeclaringClass(), NoAPIResponse.class) null;}//自动包装外层APIResposne响应Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class? extends HttpMessageConverter? selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {APIResponse apiResponse new APIResponse();apiResponse.setSuccess(true);apiResponse.setMessage(OK);apiResponse.setCode(2000);apiResponse.setData(body);return apiResponse;}
}实现了 NoAPIResponse 自定义注解。如果某些 RestController 的接口不希望实现自动包装的话可以标记这个注解
Target({ElementType.METHOD, ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
public interface NoAPIResponse {
}在 ResponseBodyAdvice 的 support 方法中我们排除了标记有这个注解的方法或类的自动响应体包装。比如对于刚才我们实现的测试客户端 client 方法不需要包装为 APIResponse就可以标记上这个注解
GetMapping(client)
NoAPIResponse
public String client(RequestParam(value error, defaultValue 0) int error){}这样我们在代码中就统一了响应体的处理不用担心有些程序员别出心裁自己搞一套。
2、要考虑接口变迁的版本控制策略
接口不可能一成不变需要根据业务需求不断增加内部逻辑。如果做大的功能调整或重构涉及参数定义的变化或是参数废弃导致接口无法向前兼容这时接口就需要有版本的概念。在考虑接口版本策略设计时我们需要注意的是最好一开始就明确版本策略并考虑在整个服务端统一版本策略。
第一版本策略最好一开始就考虑。第二版本实现方式要统一。
为了实现上面的目的我们可以通过注解的方式为接口增加基于 URL 的版本号首先创建一个注解来定义接口的版本。APIVersion 自定义注解可以应用于方法或 Controller 上
Target({ElementType.METHOD, ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
public interface APIVersion {String[] value();
}然后定义一个 APIVersionHandlerMapping 类继承 RequestMappingHandlerMapping。 public class APIVersionHandlerMapping extends RequestMappingHandlerMapping {Overrideprotected boolean isHandler(Class? beanType) {return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);}Overrideprotected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {Class? controllerClass method.getDeclaringClass();//类上的APIVersion注解APIVersion apiVersion AnnotationUtils.findAnnotation(controllerClass, APIVersion.class);//方法上的APIVersion注解APIVersion methodAnnotation AnnotationUtils.findAnnotation(method, APIVersion.class);//以方法上的注解优先if (methodAnnotation ! null) {apiVersion methodAnnotation;}String[] urlPatterns apiVersion null ? new String[0] : apiVersion.value();PatternsRequestCondition apiPattern new PatternsRequestCondition(urlPatterns);PatternsRequestCondition oldPattern mapping.getPatternsCondition();PatternsRequestCondition updatedFinalPattern apiPattern.combine(oldPattern);//重新构建RequestMappingInfomapping new RequestMappingInfo(mapping.getName(), updatedFinalPattern, mapping.getMethodsCondition(),mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(),mapping.getProducesCondition(), mapping.getCustomCondition());super.registerHandlerMethod(handler, method, mapping);}
}RequestMappingHandlerMapping 的作用是根据类或方法上的 RequestMapping 来生成 RequestMappingInfo 的实例。我们覆盖 registerHandlerMethod 方法的实现从 APIVersion 自定义注解中读取版本信息拼接上原有的、不带版本号的 URL Pattern构成新的 RequestMappingInfo来通过注解的方式为接口增加基于 URL 的版本号。
最后要通过实现 WebMvcRegistrations 接口来生效自定义的 APIVersionHandlerMapping
SpringBootApplication
public class CommonMistakesApplication implements WebMvcRegistrations {Overridepublic RequestMappingHandlerMapping getRequestMappingHandlerMapping() {return new APIVersionHandlerMapping();}
}这样就实现了在 Controller 上或接口方法上通过注解来实现以统一的 Pattern 进行版本号控制使用时
GetMapping(value /api/user)
APIVersion(v4)
public int right4() {return 4;
}访问url为 http://localhost:8080/v4/api/user
使用框架来明确 API 版本的指定策略不仅实现了标准化更实现了强制的 API 版本控制。假如我们的接口强制要求必须要有版本号可以改动APIVersionHandlerMapping代码在获取不到APIVersion注解时就给予报错提示。