第一:解決切面讀取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加上注解即可: