packagecom.hsh.common.dispatch;importcn.hutool.core.collection.CollectionUtil;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fasterxml.jackson.databind.node.ObjectNode;importorg.apache.commons.compress.utils.IOUtils;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.core.Ordered;importorg.springframework.core.annotation.Order;importorg.springframework.http.HttpStatus;importorg.springframework.util.CollectionUtils;importorg.springframework.util.ObjectUtils;importorg.springframework.util.StringUtils;importorg.springframework.web.servlet.DispatcherServlet;importorg.springframework.web.servlet.HandlerExecutionChain;importorg.springframework.web.util.ContentCachingRequestWrapper;importorg.springframework.web.util.ContentCachingResponseWrapper;importjavax.servlet.ReadListener;importjavax.servlet.ServletInputStream;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjavax.servlet.http.HttpServletResponse;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.PrintWriter;importjava.text.MessageFormat;import java.util.*;
@Order(Ordered.HIGHEST_PRECEDENCE)//最高优先级 方便拦截404什么的
public class LoggableDispatcherServlet extendsDispatcherServlet {private static final Logger logger = LoggerFactory.getLogger("HttpLogger");private static final ObjectMapper mapper = newObjectMapper();private static final long serialVersionUID = -2151909516770706554L;
@Overrideprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throwsException {//if ("GET".equals(request.getMethod())) {//super.doDispatch(request, response);//return;//}
List curlItemList = new ArrayList<>();
ContentCachingResponseWrapper responseWrapper= newContentCachingResponseWrapper(response);
setThrowExceptionIfNoHandlerFound(true);
ObjectNode rootNode=mapper.createObjectNode();
ObjectNode reqNode=mapper.createObjectNode();
ObjectNode resNode=mapper.createObjectNode();
String method=request.getMethod();
curlItemList.add(MessageFormat.format("-X ''{0}''", method));
rootNode.put("method", method);
String requestUrl=request.getRequestURL().toString();
rootNode.put("url", requestUrl);
rootNode.put("remoteAddr", request.getRemoteAddr());
rootNode.put("x-forwarded-for", request.getHeader("x-forwarded-for"));
rootNode.set("request", reqNode);
rootNode.set("response", resNode);
reqNode.set("headers", mapper.valueToTree(getRequestHeaders(request)));try{
reqNode.set("query", mapper.valueToTree(request.getParameterMap()));if ("GET".equals(method)) {
StringBuilder stringBuilder= newStringBuilder();
stringBuilder.append("'").append(requestUrl);
Enumeration parameterNames =request.getParameterNames();if(parameterNames.hasMoreElements()) {
stringBuilder.append("?");
}while(parameterNames.hasMoreElements()) {
String name=parameterNames.nextElement();
String[] values=request.getParameterValues(name);for(String value : values) {
stringBuilder.append(name).append("=").append(value).append("&");
}
}
stringBuilder.append("'");
String url=stringBuilder.toString();if (url.endsWith("&'")) {
url= url.substring(0, url.length() - 2) + "'";
}
curlItemList.add(url);super.doDispatch(request, responseWrapper);
}else{
curlItemList.add("'" + requestUrl + "'");if(isFormPost(request)) {
ContentCachingRequestWrapper bufferedServletRequestWrapper= newContentCachingRequestWrapper(request);
reqNode.set("body", mapper.valueToTree(request.getParameterMap()));
reqNode.put("bodyIsJson", false);
StringBuilder stringBuilder= newStringBuilder();
Enumeration parameterNames =request.getParameterNames();while(parameterNames.hasMoreElements()) {
String name=parameterNames.nextElement();
String[] values=request.getParameterValues(name);for(String value : values) {
stringBuilder.append(name).append("=").append(value).append("&");
}
}
String paramsStr=stringBuilder.toString();if (paramsStr.endsWith("&")) {
paramsStr= paramsStr.substring(0, paramsStr.length() - 1);
}
curlItemList.add(MessageFormat.format("--data-raw ''{0}''", paramsStr));super.doDispatch(bufferedServletRequestWrapper, responseWrapper);
}else if(isJsonPost(request)) {
BufferedServletRequestWrapper bufferedServletRequestWrapper= newBufferedServletRequestWrapper(request);
ServletInputStream inputStream=bufferedServletRequestWrapper.getInputStream();byte[] contentAsByteArray =IOUtils.toByteArray(inputStream);
reqNode.set("body", mapper.readTree(contentAsByteArray));
reqNode.put("bodyIsJson", true);
curlItemList.add(MessageFormat.format("--data-binary ''{0}''", mapper.readTree(contentAsByteArray)));super.doDispatch(bufferedServletRequestWrapper, responseWrapper);
}else if (isTextPost(request) ||isXmlPost(request)) {
BufferedServletRequestWrapper bufferedServletRequestWrapper= newBufferedServletRequestWrapper(request);
ServletInputStream inputStream=bufferedServletRequestWrapper.getInputStream();byte[] contentAsByteArray =IOUtils.toByteArray(inputStream);
reqNode.put("body", newString(contentAsByteArray));
reqNode.put("bodyIsJson", false);
curlItemList.add(MessageFormat.format("--data-binary ''{0}''", newString(contentAsByteArray)));super.doDispatch(bufferedServletRequestWrapper, responseWrapper);
}else if(isMediaPost(request)) {
reqNode.put("body", "Media Request Body ContentLength = " +request.getContentLengthLong());
reqNode.put("bodyIsJson", false);super.doDispatch(request, responseWrapper);
}else{
reqNode.put("body", "Unknown Request Body ContentLength = " +request.getContentLengthLong());
reqNode.put("bodyIsJson", false);super.doDispatch(request, responseWrapper);
}
}
HandlerExecutionChain handlerExecutionChain=getHandler(request);if (handlerExecutionChain == null) {//手动判断是不是404 不走系统流程 直接处理 因为会重定向/error
resNode.put("status", HttpStatus.NOT_FOUND.value());
logger.info(rootNode.toString());
response.setStatus(HttpStatus.NOT_FOUND.value());
PrintWriter writer=response.getWriter();
writer.write("Request path not found");
writer.flush();
writer.close();return;
}
System.out.println(handlerExecutionChain);
}finally{byte[] responseWrapperContentAsByteArray =responseWrapper.getContentAsByteArray();
responseWrapper.copyBodyToResponse();//这里有顺序 必须先读body 然后再调用这个方法 才能继续读
resNode.put("status", response.getStatus());
Map responseHeaders =getResponseHeaders(response);//这里判断错误拦截是不是吧url改成error了 如果是就做一下替换 替换的值是错误拦截器写到header里面的
String url = rootNode.get("url").asText();if (url.endsWith("/error")) {
String path= (String) responseHeaders.get("x-error-path");if (!ObjectUtils.isEmpty(path)) {
rootNode.put("url", url.replace("/error", path));
}
}
resNode.set("headers", mapper.valueToTree(responseHeaders));if (isProtoBufPost(responseWrapper) || "GET".equals(request.getMethod())) {
}else{try{
resNode.set("body", mapper.readTree(responseWrapperContentAsByteArray));
resNode.put("bodyIsJson", true);
}catch(Exception e) {
resNode.put("body", newString(responseWrapperContentAsByteArray));
resNode.put("bodyIsJson", false);
}
}
logger.info(rootNode.toPrettyString());
getCurlRequestHeaders(request, curlItemList);
logger.info("curl " + CollectionUtil.join(curlItemList, "\n "));
}
}private MapgetRequestHeaders(HttpServletRequest request) {
Map headers = new HashMap<>();
Enumeration headerNames =request.getHeaderNames();while(headerNames.hasMoreElements()) {
String headerName=headerNames.nextElement();
headers.put(headerName, request.getHeader(headerName));
}returnheaders;
}private void getCurlRequestHeaders(HttpServletRequest request, Listlist) {
Enumeration headerNames =request.getHeaderNames();while(headerNames.hasMoreElements()) {
String headerName=headerNames.nextElement();
String headerValue=request.getHeader(headerName);if (!headerValue.contains("multipart/form-data")) {
list.add(MessageFormat.format("-H ''{0}: {1}''", headerName, headerValue));
}
}
}private MapgetResponseHeaders(HttpServletResponse response) {
Map headers = new HashMap<>();
Collection headerNames =response.getHeaderNames();for(String headerName : headerNames) {
headers.put(headerName, response.getHeader(headerName));
}returnheaders;
}private booleanisFormPost(HttpServletRequest request) {
String contentType=request.getContentType();return (contentType != null && contentType.contains("x-www-form"));
}private booleanisMediaPost(HttpServletRequest request) {
String contentType=request.getContentType();if (contentType != null) {return contentType.contains("stream") || contentType.contains("image") || contentType.contains("video") || contentType.contains("audio");
}return false;
}private booleanisTextPost(HttpServletRequest request) {
String contentType=request.getContentType();if (contentType != null) {return contentType.contains("text/plain") || contentType.contains("text/xml") || contentType.contains("text/html");
}return false;
}private booleanisJsonPost(HttpServletRequest request) {
String contentType=request.getContentType();if (contentType != null) {return contentType.contains("application/json");
}return false;
}private booleanisXmlPost(HttpServletRequest request) {
String contentType=request.getContentType();if (contentType != null) {return contentType.contains("application/xml");
}return false;
}private booleanisProtoBufPost(HttpServletRequest request) {
String contentType=request.getContentType();if (contentType != null) {return contentType.contains("application") && contentType.contains("protobuf");
}return false;
}private booleanisProtoBufPost(HttpServletResponse response) {
String contentType=response.getContentType();if (contentType != null) {return contentType.contains("application") && contentType.contains("protobuf");
}return false;
}private booleanisMultipartFormDataPost(HttpServletRequest request) {
String contentType=request.getContentType();if (contentType != null) {return contentType.contains("multipart/form-data");
}return false;
}class BufferedServletInputStream extendsServletInputStream {privateByteArrayInputStream inputStream;privateServletInputStream is;public BufferedServletInputStream(byte[] buffer, ServletInputStream is) {this.is =is;this.inputStream = newByteArrayInputStream(buffer);
}
@Overridepublic intavailable() {returninputStream.available();
}
@Overridepublic intread() {returninputStream.read();
}
@Overridepublic int read(byte[] b, int off, intlen) {returninputStream.read(b, off, len);
}
@Overridepublic booleanisFinished() {returnis.isFinished();
}
@Overridepublic booleanisReady() {returnis.isReady();
}
@Overridepublic voidsetReadListener(ReadListener listener) {
is.setReadListener(listener);
}
}class BufferedServletRequestWrapper extendsHttpServletRequestWrapper {private byte[] buffer;privateServletInputStream is;public BufferedServletRequestWrapper(HttpServletRequest request) throwsIOException {super(request);this.is =request.getInputStream();
ByteArrayOutputStream byteArrayOutputStream= newByteArrayOutputStream();
byteArrayOutputStream.write(IOUtils.toByteArray(is));this.buffer =byteArrayOutputStream.toByteArray();
}
@OverridepublicServletInputStream getInputStream() {return new BufferedServletInputStream(this.buffer, this.is);
}
}
}