SpringBoot同一API魔改

手机游戏开发者 2024-9-30 17:38:41 94 0 来自 中国
设置注解

package com.example.demo.api;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface API {    boolean request() default true;    boolean response() default true;}以注解的情势对接口举行设置,可设置在Controller的类或方法上。request属性为true,表现哀求体需要分公有域和私有域,私有域数据封装在data节点内;为false,表现不区分公有域和私有域。response同理,如果属性为true,会将返回的业务数据封装在data节点内。
相应码的界说

package com.example.demo.api;import cn.hutool.core.util.StrUtil;import com.fasterxml.jackson.annotation.JsonCreator;import com.fasterxml.jackson.annotation.JsonValue;import java.util.Arrays;public enum State {    SUCCESS(0, "success"),    VALID_FAIL(100, "valid fail"),    PASSWORD_ERROR(101, "password error"),    UNKNOWN_ERROR(999, "unknow error");    @JsonValue    private int code;    private String message;    State(int code, String message) {        this.code = code;        this.message = message;    }    public int getCode() {        return code;    }    public String getMessage() {        return message;    }    @Override    public String toString() {        return String.format("[%03d] %s", code, message);    }    public void tryThrow() {        throw new StatefulException(this);    }    public void tryThrow(String message) {        throw new StatefulException(this, message);    }    public void tryThrow(String messageTemplate, Object... params) {        throw new StatefulException(this, messageTemplate, params);    }    public void tryThrow(Throwable cause) {        throw new StatefulException(this, cause);    }    public void isTrue(boolean expression) {        isFalse(!expression);    }    public void isTrue(boolean expression, String message) {        isFalse(!expression, message);    }    public void isTrue(boolean expression, String messageTemplate, Object... params) {        isFalse(!expression, messageTemplate, params);    }    public void isFalse(boolean expression) {        if (expression) {            throw new StatefulException(this);        }    }    public void isFalse(boolean expression, String message) {        if (expression) {            throw new StatefulException(this, message);        }    }    public void isFalse(boolean expression, String messageTemplate, Object... params) {        if (expression) {            throw new StatefulException(this, messageTemplate, params);        }    }    public <T> T notNull(T obj) {        if (obj == null) {            throw new StatefulException(this);        }        return obj;    }    public <T> T notNull(T obj, String message) {        if (obj == null) {            throw new StatefulException(this, message);        }        return obj;    }    public <T> T notNull(T obj, String messageTemplate, Object... params) {        if (obj == null) {            throw new StatefulException(this, messageTemplate, params);        }        return obj;    }    public <T extends CharSequence> T notBlank(CharSequence str) {        if (StrUtil.isBlank(str)) {            throw new StatefulException(this);        }        return (T) str;    }    public <T extends CharSequence> T notBlank(CharSequence str, String message) {        if (StrUtil.isBlank(str)) {            throw new StatefulException(this, message);        }        return (T) str;    }    public <T extends CharSequence> T notBlank(CharSequence str, String messageTemplate, Object... params) {        if (StrUtil.isBlank(str)) {            throw new StatefulException(this, messageTemplate, params);        }        return (T) str;    }    public <T> List<T> notEmpty(List<T> list) {        if (list == null || list.isEmpty()) {            tryThrow();        }        return list;    }    public <T> List<T> notEmpty(List<T> list, String errorMessage) {        if (list == null || list.isEmpty()) {            tryThrow(errorMessage);        }        return list;    }    public <T> List<T> notEmpty(List<T> list, String messageTemplate, Object... params) {        if (list == null || list.isEmpty()) {            tryThrow(messageTemplate, params);        }        return list;    }    @JsonCreator    public static State valueOf(int code) {        return Arrays.stream(values()).filter(state -> state.getCode() == code).findAny().orElse(null);    }}界说了错误码、错误信息,以及一些断言。使用断言可以抛出包罗错误码和错误信息的非常
支持相应码的非常

package com.example.demo.api;import cn.hutool.core.util.StrUtil;public class StatefulException extends RuntimeException {    private State state;    public StatefulException(State state) {        this.state = state;    }    public StatefulException(State state, String message) {        super(message);        this.state = state;    }    public StatefulException(State state, String messageTemplate, Object... params) {        super(StrUtil.format(messageTemplate, params));        this.state = state;    }    public StatefulException(State state, Throwable cause) {        super(cause);        this.state = state;    }    public State getState() {        return state;    }    public void setState(State state) {        this.state = state;    }    @Override    public String toString() {        return StrUtil.isBlank(getMessage()) ? state.toString() : state + ": " + getMessage();    }}公有域的界说

package com.example.demo.api;import java.io.Serializable;public class PublicDomain<T> implements Serializable {    private T data;    public PublicDomain() {    }    public PublicDomain(T data) {        this.data = data;    }    public T getData() {        return data;    }    public void setData(T data) {        this.data = data;    }}将哀求和相应分为公有域部门和私有域部门。相应中公有域包罗相应码、相应信息和由data封装的私有域。
相应体的界说

package com.example.demo.api;import com.fasterxml.jackson.annotation.JsonInclude;import com.fasterxml.jackson.annotation.JsonPropertyOrder;@JsonPropertyOrder({"resCode", "resMessage", "errorMessage", "data"})public class ResultVo<T> extends PublicDomain<T> {    // 相应状态码    private int resCode;    // 相应状态信息    private String resMessage;    // 错误详情    @JsonInclude(JsonInclude.Include.NON_NULL)    private String errorMessage;    public ResultVo() {        this(State.SUCCESS);    }    public ResultVo(T data) {        super(data);        this.resCode = State.SUCCESS.getCode();        this.resMessage = State.SUCCESS.getMessage();    }    public ResultVo(State state) {        this.resCode = state.getCode();        this.resMessage = state.getMessage();    }    public ResultVo(StatefulException statefulException) {        State state = statefulException.getState();        this.resCode = state.getCode();        this.resMessage = state.getMessage();        this.errorMessage = statefulException.getMessage();    }    public ResultVo(State state, Throwable ex) {        this.resCode = state.getCode();        this.resMessage = state.getMessage();        this.errorMessage = ex.getMessage();    }    public int getResCode() {        return resCode;    }    public void setResCode(int resCode) {        this.resCode = resCode;    }    public String getResMessage() {        return resMessage;    }    public void setResMessage(String resMessage) {        this.resMessage = resMessage;    }    public String getErrorMessage() {        return errorMessage;    }    public void setErrorMessage(String errorMessage) {        this.errorMessage = errorMessage;    }}哀求体的处置惩罚

package com.example.demo.api;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.MethodParameter;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpInputMessage;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.util.StreamUtils;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Type;import java.nio.charset.StandardCharsets;@RestControllerAdvice(basePackages = "com.example.demo")public class RequesstApiAdvice extends RequestBodyAdviceAdapter {    @Autowired    private ObjectMapper objectMapper;    @Override    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {        API apiAnn = methodParameter.hasMethodAnnotation(API.class) ?                methodParameter.getMethodAnnotation(API.class) : methodParameter.getDeclaringClass().getAnnotation(API.class);        return apiAnn != null && apiAnn.request() && !PublicDomain.class.equals(methodParameter.getParameterType());    }    @Override    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,                                           Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {        JsonNode jsonNode = objectMapper.readTree(inputMessage.getBody());        return new HttpInputMessage() {            @Override            public InputStream getBody() {                JsonNode dataJsonNode = State.VALID_FAIL.notNull(jsonNode.get("data"), "哀求体未包罗私有域节点data");                return new ByteArrayInputStream(dataJsonNode.toString().getBytes(StandardCharsets.UTF_8));            }            @Override            public HttpHeaders getHeaders() {                return inputMessage.getHeaders();            }        };    }}相应体的处置惩罚

相应体的处置惩罚可以基于ResponseBodyAdvice,也可以基于HandlerMethodReturnValueHandler,两种方案可以任选其一
基于ResponseBodyAdvice的实现

package com.example.demo.api;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;@RestControllerAdvice(basePackages = "com.example.demo")public class ResponseApiAdvice implements ResponseBodyAdvice {    @Override    public boolean supports(MethodParameter returnType, Class converterType) {        API apiAnn = returnType.hasMethodAnnotation(API.class) ?                returnType.getMethodAnnotation(API.class) : returnType.getDeclaringClass().getAnnotation(API.class);        return apiAnn != null && apiAnn.response();    }    @Override    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {        return body instanceof ResultVo ? body : new ResultVo<>(body);    }}基于HandlerMethodReturnValueHandler的实现

package com.example.demo.api;import org.springframework.core.MethodParameter;import org.springframework.stereotype.Component;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodReturnValueHandler;import org.springframework.web.method.support.ModelAndViewContainer;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;import java.util.ArrayList;import java.util.List;@Componentpublic class ApiHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {    private HandlerMethodReturnValueHandler handler;    public ApiHandlerMethodReturnValueHandler(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {        List<HandlerMethodReturnValueHandler> originHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();        List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>(originHandlers.size());        for (HandlerMethodReturnValueHandler originHandler : originHandlers) {            if (originHandler instanceof RequestResponseBodyMethodProcessor) {                newHandlers.add(this);                handler = originHandler;            } else {                newHandlers.add(originHandler);            }        }        requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);    }    @Override    public boolean supportsReturnType(MethodParameter returnType) {        API apiAnn = returnType.hasMethodAnnotation(API.class) ?                returnType.getMethodAnnotation(API.class) : returnType.getDeclaringClass().getAnnotation(API.class);        return handler.supportsReturnType(returnType) && apiAnn != null && apiAnn.response();    }    @Override    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {        Object response = returnValue instanceof ResultVo ? returnValue : new ResultVo<>(returnValue);        handler.handleReturnValue(response, returnType, mavContainer, webRequest);    }}HandlerMethodReturnValueHandler 的作用是对处置惩罚器的处置惩罚效果再举行一次二次加工
全局非常处置惩罚

package com.example.demo.api;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvicepublic class GlobalExceptionResolver {    private final static Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionResolver.class);    @ExceptionHandler(Exception.class)    public ResultVo exceptionHandle(Exception ex) {        LOGGER.error("error", ex);        return new ResultVo(State.UNKNOWN_ERROR, ex);    }    @ExceptionHandler(RuntimeException.class)    public ResultVo exceptionHandle(RuntimeException ex) {        LOGGER.error("valid fail", ex);        return new ResultVo(State.VALID_FAIL, ex);    }    @ExceptionHandler(StatefulException.class)    public ResultVo exceptionHandle(StatefulException ex) {        LOGGER.error(ex.getState().toString(), ex);        return new ResultVo(ex);    }}测试

package com.example.demo.user.entity;import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class User {    private String account;    private String name;    private String password;}package com.example.demo.user.controller;import com.example.demo.api.API;import com.example.demo.api.State;import com.example.demo.user.entity.User;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class UserController {    @API    @PostMapping("/login/passport")    public User test(@RequestBody User user) {        State.VALID_FAIL.notBlank(user.getAccount(), "账号为空");        State.VALID_FAIL.notBlank(user.getPassword(), "暗码为空");        State.PASSWORD_ERROR.isTrue(user.getPassword().equals("123456"), "暗码错误");        return user;    }}精确哀求
{    "data": {        "account": "demo",        "password": "123456"    }}相应
{    "resCode": 0,    "resMessage": "success",    "data": {        "account": "demo",        "name": null,        "password": "123456"    }}暗码错误哀求
{    "data": {        "account": "demo",        "password": "123"    }}相应
{    "resCode": 101,    "resMessage": "password error",    "errorMessage": "暗码错误",    "data": null}
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 05:14, Processed in 0.173833 second(s), 33 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表