springboot统一异常处理(记录请求参数等信息)

要求:记录项目中所抛出的异常信息,并保存该次请求的信息。

springboot中自带的注解@ControllerAdvice,可以帮助捕获所有controller抛出的信息,所以使用该种方式(看到有文章说带有该注解的类必须放在controller同包下,我试了,放在其他包下同样生效)。

在使用时需要记录请求参数(方便找出错原因),但是在处理异常时获取requestBody遇到问题:requestBody流只能读一次(httpServletRequest中的流只能读取一次的原因),通过接口再到这里,就获取不到了。于是想使用@ModelAttribute注解方法,在进入接口之前把requestBody取出来,加一个自定义参数,结果接口中读取不到requestBody了。于是在网上找了个方法,通过过滤器把requestBody读取后再放回去。

首先创建工具类:

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 用于重复读取requestBody
 */
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    private BufferedReader reader;

    private ServletInputStream inputStream;

    public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        loadBody(request);
    }

    private void loadBody(HttpServletRequest request) throws IOException {
        body = IOUtils.toByteArray(request.getInputStream());
        inputStream = new RequestCachingInputStream(body);
    }

    public byte[] getBody() {
        return body;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) {
            return inputStream;
        }
        return super.getInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (reader == null) {
            reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
        }
        return reader;
    }

    private static class RequestCachingInputStream extends ServletInputStream {

        private final ByteArrayInputStream inputStream;

        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }

        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener readlistener) {
        }

    }

}

过滤器filter:

import org.springframework.stereotype.Component;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
public class LedgerReportFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void destroy() {

    }


}

异常统一处理:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lenovo.lsump.ledger.ledgerpostingreportservice.domain.document.ExceptionLog;
import com.lenovo.lsump.ledger.ledgerpostingreportservice.domain.repository.mongo.ExceptionLogRepository;
import com.lenovo.lsump.ledger.ledgerpostingreportservice.filter.ContentCachingRequestWrapper;
import feign.FeignException;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {

    private static String REQUESTBODY = "requestBodyMessage";

    @Autowired
    ExceptionLogRepository exceptionLogRepository;

    @ExceptionHandler({Exception.class})
    public Map<String, Object> handleException(HttpServletRequest req, Exception e) throws Exception {
        try {
            String uri = req.getRequestURI();
            ExceptionLog exceptionLog = new ExceptionLog();
            exceptionLog.setCreateDate(new Date());
            Object body = req.getAttribute(REQUESTBODY);
            if (body != null) exceptionLog.setRequestBody(body.toString());
            exceptionLog.setUri(uri);
            Map<String, String[]> parameterMap = req.getParameterMap();
            if (!parameterMap.isEmpty()) {
                ObjectMapper objectMapper = new ObjectMapper();
                exceptionLog.setRequestParams(objectMapper.writeValueAsString(parameterMap));
            }
            Enumeration<String> headerNames = req.getHeaderNames();
            Map<String, String> headers = new HashMap<>();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                headers.put(headerName, req.getHeader(headerName));
            }
            if (!headers.isEmpty()) {
                ObjectMapper objectMapper = new ObjectMapper();
                exceptionLog.setRequestHeaders(objectMapper.writeValueAsString(headers));
            }
            exceptionLog.setMessage(e.toString());

            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw, true));
            exceptionLog.setStackTrace(sw.toString());

            exceptionLogRepository.save(exceptionLog);
        } catch (Exception ex) {
        }
        throw e;
    }


    /**
     * 由于body在接口读取后无法获取,这里把body提前取出放到参数中,在上面处理异常时使用
     */
    @ModelAttribute
    public void getBobyInfo(HttpServletRequest request) {
        //获取requestBody
        try {
            ContentCachingRequestWrapper requestWapper = null;
            if (request instanceof HttpServletRequest) {
                requestWapper = (ContentCachingRequestWrapper) request;
            }
            String body = IOUtils.toString(requestWapper.getBody(), request.getCharacterEncoding());
            request.setAttribute(REQUESTBODY, body);
        } catch (Exception e) {

        }

    }

    @ExceptionHandler(FeignException.class)
    public Map<String, Object> handleException(FeignException feignException, HttpServletResponse response) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String message = feignException.getMessage();
            if (!message.contains("{")) {
                message = feignException.contentUTF8();
            }
            String originalBody = message;
            Map<String, Object> map = objectMapper.readValue(originalBody, Map.class);
            response.setStatus((Integer) map.get("status"));
            return map;
        } catch (Exception e) {
            throw feignException;
        }
    }
}

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值