直接上代码
1.请求信息实体
import lombok.Data;
/**
* @className: AppInfo
* @description: 请求信息
* @author: sun
* @date: 2021/12/2
*/
@Data
public class AppInfo {
private String version;
private String platform;
private String requestURI;
private String requestParameters;
}
2.邮箱地址集合
import lombok.Data;
import java.util.List;
@Data
public class MailObject {
List<String> mailList;
}
3.获取请求头中参数
import com.alibaba.fastjson.JSON;
import com.xcb.fun.entity.AppInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;
@Slf4j
public class HandlerInfoGet {
/**
* @param: mapKey
* @description: 获取请求头 AppInfo 对象的方法
* @return: com.xcb.fun.entity.AppInfo
*/
public static AppInfo getHeader() {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String appInfo = request.getHeader("appInfo");
AppInfo app = new AppInfo();
if (Objects.nonNull(appInfo)) {
try {
app = JSON.parseObject(appInfo, AppInfo.class);
String method = request.getMethod();
app.setRequestURI(method + request.getRequestURI());
app.setRequestParameters(getMapToString(request.getParameterMap()));
} catch (Exception e) {
log.error(e.getMessage());
app = new AppInfo();
}
}
return app;
}
public static String getMapToString(Map<String, String[]> map) {
Set<String> keySet = map.keySet();
// 将set集合转换为数组
String[] keyArray = keySet.toArray(new String[keySet.size()]);
// 给数组排序(升序)
Arrays.sort(keyArray);
// 因为String拼接效率会很低的,所以转用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keyArray.length; i++) {
// 参数值为空,则不参与签名 这个方法trim()是去空格
if ((String.valueOf(map.get(keyArray[i]))).trim().length() > 0) {
sb.append(keyArray[i]).append("=").append(String.join("@", map.get(keyArray[i])));
}
if (i != keyArray.length - 1) {
sb.append("&");
}
}
return sb.toString();
}
}
4.邮件发送处理工具
import com.sun.mail.util.MailSSLSocketFactory;
import com.xcb.fun.aop.HandlerInfoGet;
import com.xcb.fun.entity.AppInfo;
import com.xcb.fun.entity.MailObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.text.SimpleDateFormat;
import java.util.*;
/** 邮件处理工具类 */
public class MailUtil {
private static final Logger logger = LoggerFactory.getLogger(MailUtil.class);
public static boolean sendMail(String title, String msgs, Address[] array) {
return sendMail(title, msgs, array, false);
}
public static boolean sendMail(String title, String msgs, Address[] array, boolean isHtml) {
try {
Properties props = new Properties();
logger.info("发送邮件--start");
// 关闭debug调试
props.setProperty("mail.debug", "false");
// 发送服务器需要身份验证
props.setProperty("mail.smtp.auth", "true");
// 设置邮件服务器主机名(我这用的是腾讯企业邮箱)
props.setProperty("mail.host", "smtp.exmail.qq.com");
// 设置邮件服务器端口
props.setProperty("mail.smtp.port", "465");
// 发送邮件协议名称
props.setProperty("mail.transport.protocol", "smtp");
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.ssl.socketFactory", sf);
Session session = Session.getInstance(props);
Message msg = new MimeMessage(session);
msg.setSubject(title);
StringBuilder builder = new StringBuilder();
builder.append(msgs);
builder.append("\n");
builder.append("\n时间 " + formatTime(new Date()));
if (isHtml) {
msg.setContent(builder.toString(), "text/html;charset = utf-8");
} else {
msg.setText(builder.toString());
}
// 此处设置发件人邮箱
msg.setFrom(new InternetAddress("发件人邮箱@qq.com"));
Transport transport = session.getTransport();
// (邮件服务器主机名、发件人邮箱、发件人邮箱密码)
transport.connect("smtp.exmail.qq.com", "发件人邮箱@qq.com", "发件邮箱密码/或者授权码");
transport.sendMessage(msg, array);
transport.close();
logger.info("发送邮件--end");
return true;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
public static String formatTime(Date date) {
if (date == null) {
return "";
}
Calendar c1 = Calendar.getInstance();
c1.setTime(new Date());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(date);
}
public static void sendEmail(
String serviceName,
String resultMessage,
StackTraceElement[] stackTraceElements,
String[] activeProfiles) {
String mailKey = "EXCEPTIONNOTIFICATION:EMAIL:" + String.join("@", activeProfiles);
// 缓存中获取邮箱地址
MailObject mailObject = RedisUtils.getBean(mailKey, MailObject.class);
if (Objects.nonNull(mailObject)) {
AppInfo appInfo = HandlerInfoGet.getHeader();
StringBuffer stringBuffer = new StringBuffer();
if (Objects.nonNull(appInfo)) {
stringBuffer.append("请求端:");
stringBuffer.append(appInfo.getPlatform());
stringBuffer.append("\n请求端版本:");
stringBuffer.append(appInfo.getVersion());
stringBuffer.append("\n请求接口:");
stringBuffer.append(appInfo.getRequestURI());
stringBuffer.append("\n请求头参数:");
stringBuffer.append(appInfo.getRequestParameters());
stringBuffer.append("\n");
}
stringBuffer.append(serviceName);
stringBuffer.append("服务出问题了快处理,原因:\n");
stringBuffer.append(StringUtils.isBlank(resultMessage) ? "未知异常" : resultMessage);
if (Objects.nonNull(stackTraceElements)) {
stringBuffer.append("\n报错信息:");
Arrays.asList(stackTraceElements)
.forEach(
e -> {
stringBuffer.append(e.toString());
stringBuffer.append("\n");
});
}
try {
List<String> mailList = mailObject.getMailList();
// 添加收件人地址 多个收件人存放list中
List<Address> list = new ArrayList<Address>();
for (String mail : mailList) {
list.add(new InternetAddress(mail));
}
Address[] senderList = list.toArray(new Address[list.size()]);
MailUtil.sendMail(
formatTime(new Date()) + " " + serviceName + "接口异常及时处理",
stringBuffer.toString(),
senderList);
} catch (AddressException e) {
logger.error(e.getMessage(), e);
}
}
}
public static MailObject getMailObject(String activeProfile) {
String mailKey = "EXCEPTIONNOTIFICATION:EMAIL:" + activeProfile;
return RedisUtils.getBean(mailKey, MailObject.class);
}
public static MailObject saveMailObject(MailObject mailObject, String activeProfile) {
String mailKey = "EXCEPTIONNOTIFICATION:EMAIL:" + activeProfile;
RedisUtils.saveBean(mailKey, mailObject);
return RedisUtils.getBean(mailKey, MailObject.class);
}
}
5.拦截所有异常处理
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xcb.basics.domain.enums.MicroServiceErrorCode;
import com.xcb.fun.util.MailUtil;
import feign.FeignException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.nio.file.AccessDeniedException;
import java.util.stream.Collectors;
/** 统一异常处理 */
@RestControllerAdvice
public class ExceptionTranslator {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionTranslator.class);
@Autowired ObjectMapper objectMapper;
/** 参数校验失败异常 */
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public MicroServiceErrorCode processValidationError(MethodArgumentNotValidException ex) {
LOGGER.error("Exception with cause = {} and exception {}", ex.getCause(), ex.getMessage());
String description =
ex.getBindingResult().getFieldErrors().stream()
.map(t -> t.getDefaultMessage())
.collect(Collectors.joining(" "));
return new MicroServiceErrorCode("400", description);
}
/** 无权限异常 */
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public MicroServiceErrorCode processAccessDeniedException(AccessDeniedException ex) {
LOGGER.error("Exception with cause = {} and exception {}", ex.getCause(), ex.getMessage());
return new MicroServiceErrorCode(String.valueOf(HttpStatus.FORBIDDEN.value()), ex.getMessage());
}
/** feign异常 */
@ExceptionHandler(FeignException.class)
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<MicroServiceErrorCode> processFeignException(FeignException ex) {
LOGGER.error("Exception with cause = {} and exception {}", ex.getCause(), ex.getMessage());
String contentStr = "content:";
MicroServiceErrorCode microServiceErrorCode = null;
if (StringUtils.contains(ex.getMessage(), contentStr)) {
String str =
ex.getMessage()
.substring(StringUtils.indexOf(ex.getMessage(), contentStr) + contentStr.length());
try {
microServiceErrorCode = objectMapper.readValue(str, MicroServiceErrorCode.class);
} catch (Exception e) {
LOGGER.error("Exception with cause = {} and exception {}", ex.getCause(), ex.getMessage());
microServiceErrorCode.setCode(String.valueOf(ex.status()));
microServiceErrorCode.setMessage(ex.getMessage());
}
} else {
microServiceErrorCode.setCode(String.valueOf(ex.status()));
microServiceErrorCode.setMessage(ex.getMessage());
}
return new ResponseEntity<>(microServiceErrorCode, HttpStatus.valueOf(ex.status()));
}
/** 统一server异常 200 */
@ExceptionHandler(XcbServerException.class)
@ResponseStatus(HttpStatus.OK)
public MicroServiceErrorCode processServerException(XcbServerException ex) {
LOGGER.error(
"XcbServerException with cause = {} and exception {}", ex.getCause(), ex.getMessage());
return new MicroServiceErrorCode(ex.getCode(), ex.getMessage());
}
// 注入对象
@Autowired private Environment env;
/** 其他异常 */
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.OK)
public MicroServiceErrorCode processRuntimeException(Exception ex) throws Exception {
LOGGER.error("Exception with cause = {} and exception {}", ex.getCause(), ex.getMessage());
MailUtil.sendEmail(
env.getProperty("spring.application.name"),
ex.getMessage(),
ex.getStackTrace(),
env.getActiveProfiles());
return new MicroServiceErrorCode("400", ex.getMessage());
}
}
6.所需依赖
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.3</version>
</dependency>