日志切面和统一异常处理
第一:解决切面读取request的参数报流关闭的问题
现在开发的项目是基于SpringBoot的maven项目,拦截器的使用很多时候是必不可少的,当有需要需要你对body中的值进行校验,例如加密验签、防重复提交、内容校验等等。
当你开开心心的在拦截器中通过request.getInputStream();获取到body中的信息后,你会发现你在controller中使用了@RequestBody注解获取参数报如下错误:stream closed
转自:https://blog.csdn.net/zhibo_lv/article/details/81875705
可以解决切面读取request的参数报错的问题,原因在于@RequestBody已经读取一次流了
LogAspect:
package com.cicmdb.aspect; import com.cicmdb.common.ErrorMessageUtil; import com.cicmdb.common.ResultModel; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.URLDecoder; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * 记录调用Controller的日志 */ @Aspect @Component public class LogAspect { private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); private static final ThreadLocal<Long> timeTreadLocal = new ThreadLocal<>(); @Pointcut("execution(* com.cicmdb.*..*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)") public void log() { } @Before("log()") public void before(JoinPoint joinPoint) throws IOException { timeTreadLocal.set(System.currentTimeMillis()); // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); // 获取请求的request HttpServletRequest request = attributes.getRequest(); // 获取所有请求的参数,封装为map对象 // Map<String,Object> parameterMap = getParameterMap(request); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); // 获取被拦截的方法 Method method = methodSignature.getMethod(); // 获取被拦截的方法名 String methodName = method.getName(); logger.info("AOP begin ,请求开始方法 :{}", method.getDeclaringClass() + "." + methodName + "()"); // 获取所有请求参数key和value String keyValue = getReqParameter(request); logger.info("请求url = {}", request.getRequestURL().toString()); logger.info("请求方法requestMethod = {}", request.getMethod()); logger.info("请求资源uri = {}", request.getRequestURI()); logger.info("所有的请求参数 key:value = {}", keyValue); } @After("log()") public void after() { logger.info("aop的after()方法"); } // controller请求结束返回时调用 @AfterReturning(returning = "result", pointcut = "log()") public Object afterReturn(Object result) { logger.info("AOP afterReturn,返回值result = {}", result.toString()); // System.out.println("返回值="+result.toString()); long startTime = timeTreadLocal.get(); double callTime = (System.currentTimeMillis() - startTime) / 1000.0; logger.info("调用controller花费时间time = {}s", callTime); return result; } /** * 获取所有请求参数,封装为map对象 * * @return */ public Map<String, Object> getParameterMap(HttpServletRequest request) { if (request == null) { return null; } Enumeration<String> enumeration = request.getParameterNames(); Map<String, Object> parameterMap = new HashMap<String, Object>(); StringBuilder stringBuilder = new StringBuilder(); while (enumeration.hasMoreElements()) { String key = enumeration.nextElement(); String value = request.getParameter(key); String keyValue = key + " : " + value + " ; "; stringBuilder.append(keyValue); parameterMap.put(key, value); } return parameterMap; } public String getReqParameter(HttpServletRequest request) throws IOException { if (request == null) { return null; } else { return JsonReq(request); } } public static String JsonReq(HttpServletRequest request) { BufferedReader br; StringBuilder sb = null; String reqBody = null; try { br = new BufferedReader(new InputStreamReader(request.getInputStream())); String line = null; sb = new StringBuilder(); while ((line = br.readLine()) != null) { sb.append(line); } reqBody = URLDecoder.decode(sb.toString(), "UTF-8"); reqBody = reqBody.substring(reqBody.indexOf("{")); request.setAttribute("inputParam", reqBody); return reqBody; } catch (IOException e) { e.printStackTrace(); return "jsonerror"; } } @Around("execution(public * com.cicmdb.*.controller.*.*(..))") public ResultModel serviceAOP(ProceedingJoinPoint pjp) throws Throwable { try { return (ResultModel) pjp.proceed(); } catch (RuntimeException e) {// controller类抛出的异常在这边捕获 // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); // 获取请求的request HttpServletRequest request = attributes.getRequest(); // 获取所有请求的参数,封装为map对象 // Map<String,Object> parameterMap = getParameterMap(request); MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); // 获取被拦截的方法 Method method = methodSignature.getMethod(); // 获取被拦截的方法名 String methodName = method.getName(); Map<String, String> errorMessage = ErrorMessageUtil.getErrorMessage(); String message = errorMessage.get(methodName); // 获取所有请求参数key和value String keyValue = getReqParameter(request); logger.error("业务 = {}", message); logger.error("错误信息 = {}", e); logger.error("请求url = {}", request.getRequestURL().toString()); logger.error("请求方法requestMethod = {}", request.getMethod()); logger.error("请求资源uri = {}", request.getRequestURI()); logger.error("所有的请求参数 key:value = {}", keyValue); ResultModel rm = new ResultModel(); rm.setData(null); rm.setMessage(message + "失败!"); rm.setSuccess(false); return rm; } } }
ServiceAnno:
package com.cicmdb.common; import java.lang.annotation.*; @Documented @Target(ElementType.METHOD) @Inherited @Retention(RetentionPolicy.RUNTIME ) public @interface ServiceAnno { /** * 值 * @return */ String operationName(); }
ErrorMessageUtil:
package com.cicmdb.common; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import com.cicmdb.manage.controller.AlaimAlterationController; import com.cicmdb.manage.controller.InformationController; import com.cicmdb.modelManage.controller.ModelListController; import com.cicmdb.modelManage.controller.ResourceTypeController; import com.cicmdb.modelManage.controller.TypeRelationController; import com.cicmdb.otherResourceManage.controller.ContractManageController; import com.cicmdb.otherResourceManage.controller.ManufacurerManageController; import com.cicmdb.otherResourceManage.controller.MyResourceController; import com.cicmdb.otherResourceManage.controller.PersonManageController; import com.cicmdb.resource.controller.FileDownloadController; import com.cicmdb.resource.controller.FileUploadController; import com.cicmdb.resource.controller.ResourceController; public class ErrorMessageUtil { public static void main(String[] args) { getErrorMessage(); } public static Map<String, String> getErrorMessage() { Map<String, String> hm = new HashMap<>(); // 此处要用反射将字段中的注解解析出来 Class<AlaimAlterationController> clz = AlaimAlterationController.class; // 解析方法上的注解 Method[] methods = clz.getDeclaredMethods(); for (Method method : methods) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<InformationController> clz1 = InformationController.class; // 解析方法上的注解 Method[] methods1 = clz1.getDeclaredMethods(); for (Method method : methods1) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<ModelListController> clz2 = ModelListController.class; // 解析方法上的注解 Method[] methods2 = clz2.getDeclaredMethods(); for (Method method : methods2) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<ResourceTypeController> clz3 = ResourceTypeController.class; // 解析方法上的注解 Method[] methods3 = clz3.getDeclaredMethods(); for (Method method : methods3) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<TypeRelationController> clz4 = TypeRelationController.class; // 解析方法上的注解 Method[] methods4 = clz4.getDeclaredMethods(); for (Method method : methods4) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<ContractManageController> clz5 = ContractManageController.class; // 解析方法上的注解 Method[] methods5 = clz5.getDeclaredMethods(); for (Method method : methods5) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<ManufacurerManageController> clz6 = ManufacurerManageController.class; // 解析方法上的注解 Method[] methods6 = clz6.getDeclaredMethods(); for (Method method : methods6) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<MyResourceController> clz7 = MyResourceController.class; // 解析方法上的注解 Method[] methods7 = clz7.getDeclaredMethods(); for (Method method : methods7) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<PersonManageController> clz8 = PersonManageController.class; // 解析方法上的注解 Method[] methods8 = clz8.getDeclaredMethods(); for (Method method : methods8) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<FileDownloadController> clz9 = FileDownloadController.class; // 解析方法上的注解 Method[] methods9 = clz9.getDeclaredMethods(); for (Method method : methods9) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<FileUploadController> clz10 = FileUploadController.class; // 解析方法上的注解 Method[] methods10 = clz10.getDeclaredMethods(); for (Method method : methods10) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } // 此处要用反射将字段中的注解解析出来 Class<ResourceController> clz11 = ResourceController.class; // 解析方法上的注解 Method[] methods11 = clz11.getDeclaredMethods(); for (Method method : methods11) { boolean methodHasAnno = method.isAnnotationPresent(ServiceAnno.class); if (methodHasAnno) { // 得到注解 ServiceAnno methodAnno = method.getAnnotation(ServiceAnno.class); // 输出注解属性 String value = methodAnno.operationName(); hm.put(method.getName(), value); } } return hm; } }
然后在controller加上注解即可: