Commit 42823e3e authored by 赵啸非's avatar 赵啸非

修改构建api文档

parent c32b5a83
package com.mortals.xhx.base.framework.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.mortals.framework.common.code.YesNo;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.core.io.InputStreamSource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Spring boot 控制器 请求日志,方便代码调试
*
* @author zxfei
*/
@Slf4j
@Aspect
@Configuration(proxyBeanMethods = false)
@Profile({"test"})
public class RequestLogAspect {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* AOP 环切 控制器 R 返回值
*
* @param point JoinPoint
* @return Object
* @throws Throwable 异常
*/
@Around("execution(* com.mortals..*Controller.*(..)) || execution(* com.mortals..*Feign.*(..))")
public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
Object[] args = point.getArgs();
// 请求参数处理
final Map<String, Object> paraMap = new HashMap<>(16);
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = getMethodParameter(method, i);
// PathVariable 参数跳过
PathVariable pathVariable = methodParam.getParameterAnnotation(PathVariable.class);
if (pathVariable != null) {
continue;
}
RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
String parameterName = methodParam.getParameterName();
if (parameterName.equals("binder")) {
continue;
}
Object value = args[i];
// 如果是body的json则是对象
if (requestBody != null && value != null) {
paraMap.putAll(BeanMap.create(value));
continue;
}
// 处理 List
if (value instanceof List) {
value = ((List) value).get(0);
}
// 处理 参数
if (value instanceof HttpServletRequest) {
paraMap.putAll(((HttpServletRequest) value).getParameterMap());
} else if (value instanceof WebRequest) {
paraMap.putAll(((WebRequest) value).getParameterMap());
} else if (value instanceof MultipartFile) {
MultipartFile multipartFile = (MultipartFile) value;
String name = multipartFile.getName();
String fileName = multipartFile.getOriginalFilename();
paraMap.put(name, fileName);
} else if (value instanceof HttpServletResponse) {
} else if (value instanceof InputStream) {
} else if (value instanceof InputStreamSource) {
} else if (value instanceof List) {
List<?> list = (List<?>) value;
AtomicBoolean isSkip = new AtomicBoolean(false);
for (Object o : list) {
if ("StandardMultipartFile".equalsIgnoreCase(o.getClass().getSimpleName())) {
isSkip.set(true);
break;
}
}
if (isSkip.get()) {
paraMap.put(parameterName, "此参数不能序列化为json");
continue;
}
} else {
// 参数名
RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class);
String paraName;
if (requestParam != null && StringUtils.isNotBlank(requestParam.value())) {
paraName = requestParam.value();
} else {
paraName = methodParam.getParameterName();
}
paraMap.put(paraName, value);
}
}
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
String requestURI = Objects.requireNonNull(request).getRequestURI();
String requestMethod = request.getMethod();
// 构建成一条长 日志,避免并发下日志错乱
StringBuilder beforeReqLog = new StringBuilder(300);
// 日志参数
List<Object> beforeReqArgs = new ArrayList<>();
//beforeReqLog.append("\n\n================ Request Start ================\n");
// 打印路由
beforeReqLog.append("Request ===> {}: {}");
beforeReqArgs.add(requestMethod);
beforeReqArgs.add(requestURI);
// 请求参数
if (paraMap.isEmpty()) {
beforeReqLog.append("\n");
} else {
beforeReqLog.append(" Parameters: {}\n");
beforeReqArgs.add(JSON.toJSONString(paraMap, SerializerFeature.DisableCircularReferenceDetect));
}
// 打印请求头
/* Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String headerName = headers.nextElement();
String headerValue = request.getHeader(headerName);
beforeReqLog.append("===Headers=== {} : {}\n");
beforeReqArgs.add(headerName);
beforeReqArgs.add(headerValue);
}*/
//beforeReqLog.append("================ Request End ================\n");
// 打印执行时间
long startNs = System.nanoTime();
log.info(beforeReqLog.toString(), beforeReqArgs.toArray());
// aop 执行后的日志
StringBuilder afterReqLog = new StringBuilder(200);
// 日志参数
List<Object> afterReqArgs = new ArrayList<>();
//afterReqLog.append("\n\n================ Response Start ================\n");
try {
Object result = point.proceed();
// 打印返回结构体
afterReqLog.append("Response===> {}\n");
afterReqArgs.add(StringEscapeUtils.unescapeJava(JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect)));
return result;
} finally {
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
afterReqLog.append("=== {}: {} ({} ms)\n");
afterReqArgs.add(requestMethod);
afterReqArgs.add(requestURI);
afterReqArgs.add(tookMs);
//afterReqLog.append("================ Response End ================\n");
log.info(afterReqLog.toString(), afterReqArgs.toArray());
}
}
/**
* 获取方法参数信息
*
* @param method 方法
* @param parameterIndex 参数序号
* @return {MethodParameter}
*/
public MethodParameter getMethodParameter(Method method, int parameterIndex) {
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return methodParameter;
}
public static void main(String[] args) {
String str = "execution(* com.mortals..*Controller.*(..) && " +
"(@within(org.springframework.stereotype.Controller) || " +
"@within(org.springframework.web.bind.annotation.RestController))";
System.out.println(str);
}
}
\ No newline at end of file
package com.mortals.xhx.base.framework.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.util.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
/**
* 打印每个请求的入参、出参等信息
*
* @author: zxfei
* @date: 2022/4/20 9:24
*/
@Aspect
@Component
@Slf4j
@Order(1)
@Profile({ "develop", "test"})
public class WebLogAspect {
@Pointcut("execution(public * com.mortals..*Controller.*(..))")
public void webLog() {
}
@Pointcut("execution(public * com.mortals.xhx.base.framework.exception.ExceptionHandle.*(..))")
public void exceptions() {
}
/**
* 只在进入controller时记录请求信息
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// log.debug("请求路径 {} ,进入方法 {}", request.getRequestURI(), joinPoint.getSignature().getDeclaringTypeName() + ":" + joinPoint.getSignature().getName());
MDC.put("req", getRequestInfo(request).toJSONString());
MDC.put("startTime", String.valueOf(System.currentTimeMillis()));
}
/**
* 打印请求日志
*/
@AfterReturning(pointcut = "webLog()|| exceptions()", returning = "result")
public void afterReturning(Object result) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Map<String, String> map = MDC.getCopyOfContextMap();
if (map != null) {
String startTime = map.getOrDefault("startTime", String.valueOf(System.currentTimeMillis()));
long takeTime = (System.currentTimeMillis() - Long.parseLong(startTime)) / 100;
log.debug(" \n 请求路径:{} \n 耗时:{}ms \n 请求报文:{} \n 响应报文:{}"
, request.getRequestURI(), takeTime, map.getOrDefault("req", ""), result == null ? "" : result.toString());
}
}
/**
* 读取请求信息,如果是表单则转换为json
*/
private JSONObject getRequestInfo(HttpServletRequest req) {
JSONObject requestInfo = new JSONObject();
try {
StringBuffer requestURL = req.getRequestURL();
requestInfo.put("requestURL", requestURL);
String method = req.getMethod();
requestInfo.put("method", method);
if (req.getQueryString() != null) {
requestInfo.put("queryString", URLDecoder.decode(req.getQueryString(), "UTF-8"));
}
String remoteAddr = req.getRemoteAddr();
requestInfo.put("remoteAddr", remoteAddr);
if (req instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req;
String bodyStr = new String(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
if (bodyStr.startsWith("{")) {
JSONObject jsonObject = JSON.parseObject(bodyStr);
requestInfo.put("requestBody", jsonObject);
}
}
} catch (Exception e) {
log.error("解析请求失败", e);
requestInfo.put("parseError", e.getMessage());
}
return requestInfo;
}
}
...@@ -39,6 +39,7 @@ public class AuthJsonInterceptor extends BaseInterceptor { ...@@ -39,6 +39,7 @@ public class AuthJsonInterceptor extends BaseInterceptor {
if (config.needCheckAuth(uri)) { if (config.needCheckAuth(uri)) {
boolean auth = this.checkAuth(request, uri, config.getSecurityKey()); boolean auth = this.checkAuth(request, uri, config.getSecurityKey());
if (!auth) { if (!auth) {
//不存在时候 如果是管理员也不做拦截
CookieInfo cookie = CookieService.getLoginCookie(request, config.getSecurityKey()); CookieInfo cookie = CookieService.getLoginCookie(request, config.getSecurityKey());
if (cookie == null || cookie.getUser() == null) { if (cookie == null || cookie.getUser() == null) {
writeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, "用户未登录或登录失效,请重新登录"); writeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, "用户未登录或登录失效,请重新登录");
......
...@@ -60,6 +60,8 @@ cookie: ...@@ -60,6 +60,8 @@ cookie:
key: 026db82420614469897fcc2dc1b4ce38 key: 026db82420614469897fcc2dc1b4ce38
domain: base.testnew.com domain: base.testnew.com
port: 111 port: 111
platform:
mark: base-platform
upload: upload:
path: @profiles.filepath@ path: @profiles.filepath@
# token配置 # token配置
......
...@@ -56,9 +56,12 @@ ...@@ -56,9 +56,12 @@
<!-- level 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前logger将会继承上级的级别--> <!-- level 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前logger将会继承上级的级别-->
<!-- additivity 是否向上级logger传递打印信息。默认是true。false:表示只用当前logger的appender-ref。true:表示当前logger的appender-ref和rootLogger的appender-ref都有效。--> <!-- additivity 是否向上级logger传递打印信息。默认是true。false:表示只用当前logger的appender-ref。true:表示当前logger的appender-ref和rootLogger的appender-ref都有效。-->
<logger name="com.mortals.xhx" level="INFO" additivity="false"> <logger name="com.mortals" level="DEBUG" additivity="false">
<appender-ref ref="console"/> <appender-ref ref="console"/>
<appender-ref ref="fileInfo"/> <appender-ref ref="fileInfo"/>
<appender-ref ref="fileError"/> <appender-ref ref="fileError"/>
</logger> </logger>
<logger name="org.mybatis" level="INFO"/>
<logger name="org.springframework" level="WARN"/>
<logger name="org.apache" level="WARN"/>
</configuration> </configuration>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment