增加网关统一异常处理,不在ControllerAOP处理

This commit is contained in:
wangiegie@gmail.com 2017-12-25 20:53:49 +08:00
parent 0c8044860d
commit 85c50241c6
12 changed files with 288 additions and 47 deletions

View File

@ -26,13 +26,12 @@ public class LogReceiveListener {
@RabbitHandler
public void receive(LogVo logVo) {
System.out.println(logVo.getSysLog());
SysLog sysLog = logVo.getSysLog();
if (StringUtils.isNotEmpty(logVo.getToken())) {
String username = UserUtils.getUserName(logVo.getToken());
MDC.put(KEY_USER, username);
sysLog.setCreateBy(username);
sysLogService.insert(sysLog);
MDC.remove(KEY_USER);
}
String username = UserUtils.getUserName(logVo.getToken());
MDC.put(KEY_USER, username);
sysLog.setCreateBy(username);
sysLogService.insert(sysLog);
MDC.remove(KEY_USER);
}
}

View File

@ -47,6 +47,7 @@ public class LogController extends BaseController {
*/
@DeleteMapping("/{id}")
public R<Boolean> delete(@PathVariable Integer id) {
int o = 11 / 0;
return new R<>(sysLogService.updateByLogId(id));
}
}

View File

@ -17,6 +17,7 @@
<result column="params" property="params" />
<result column="time" property="time" />
<result column="del_flag" property="delFlag" />
<result column="exception" property="exception" />
</resultMap>
<!-- 通用查询结果列 -->

View File

@ -0,0 +1,63 @@
package com.github.pig.admin.controller;
import com.github.pig.admin.dto.UserDto;
import com.xiaoleilu.hutool.http.HttpUtil;
import com.xiaoleilu.hutool.json.JSON;
import com.xiaoleilu.hutool.json.JSONUtil;
import org.apache.commons.collections.MapUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* @author lengleng
* @date 2017/12/25
* UserController单元测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void testUserGet() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.username").value("admin"));
}
@Test
public void testUserAdd() throws Exception {
UserDto userDto = new UserDto();
userDto.setRole(1);
userDto.setNewpassword1("@1312312");
mockMvc.perform(MockMvcRequestBuilders.post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(JSONUtil.toJsonStr(userDto)))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
public void testPostEdu() throws Exception {
System.out.println(HttpUtil.post("http://eco.ahau.edu.cn/jc/dtrans", MapUtils.EMPTY_MAP));
}
}

View File

@ -6,6 +6,7 @@ import com.github.pig.common.util.UserUtils;
import com.github.pig.common.util.exception.CheckException;
import com.github.pig.common.util.exception.UnloginException;
import com.github.pig.common.vo.UserVo;
import com.xiaoleilu.hutool.lang.Console;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@ -102,7 +103,8 @@ public class ControllerAop {
result = pjp.proceed();
logger.info(pjp.getSignature() + "use time:" + (System.currentTimeMillis() - startTime));
} catch (Throwable e) {
result = handlerException(pjp, e);
logger.error("异常信息:", e);
throw new RuntimeException(e);
} finally {
if (StringUtils.isNotEmpty(username)) {
UserUtils.clearAllUserInfo();
@ -111,23 +113,4 @@ public class ControllerAop {
return result;
}
private R<?> handlerException(ProceedingJoinPoint pjp, Throwable e) {
R<?> result = new R();
// 已知异常
if (e instanceof CheckException) {
result.setMsg(e.getLocalizedMessage());
result.setCode(R.FAIL);
} else if (e instanceof UnloginException) {
result.setMsg("Unlogin");
result.setCode(R.NO_LOGIN);
} else {
logger.error(pjp.getSignature() + " error ", e);
result.setMsg(e.toString());
result.setCode(R.FAIL);
}
return result;
}
}

View File

@ -69,6 +69,11 @@ public class SysLog implements Serializable {
*/
private String delFlag;
/**
* 异常信息
*/
private String exception;
public Integer getId() {
return id;
}
@ -173,20 +178,31 @@ public class SysLog implements Serializable {
this.delFlag = delFlag;
}
public String getException() {
return exception;
}
public void setException(String exception) {
this.exception = exception;
}
@Override
public String toString() {
return "SysLog{" +
", id=" + id +
", type=" + type +
", title=" + title +
", createBy=" + createBy +
", createDate=" + createTime +
"id=" + id +
", type='" + type + '\'' +
", title='" + title + '\'' +
", createBy='" + createBy + '\'' +
", createTime=" + createTime +
", updateTime=" + updateTime +
", remoteAddr=" + remoteAddr +
", userAgent=" + userAgent +
", requestUri=" + requestUri +
", method=" + method +
", params=" + params +
"}";
", remoteAddr='" + remoteAddr + '\'' +
", userAgent='" + userAgent + '\'' +
", requestUri='" + requestUri + '\'' +
", method='" + method + '\'' +
", params='" + params + '\'' +
", time=" + time +
", delFlag='" + delFlag + '\'' +
", exception='" + exception + '\'' +
'}';
}
}

View File

@ -0,0 +1,74 @@
package com.github.pig.common.vo;
import com.alibaba.fastjson.annotation.JSONField;
import java.io.Serializable;
/**
* @author lengleng
* @date 2017/12/25
* spring boot 的异常对象
*/
public class ErrorPojo implements Serializable {
@JSONField(name = "timestamp")
private long timestamp;
@JSONField(name = "status")
private int status;
@JSONField(name = "error")
private String error;
@JSONField(name = "exception")
private String exception;
@JSONField(name = "message")
private String message;
@JSONField(name = "path")
private String path;
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getException() {
return exception;
}
public void setException(String exception) {
this.exception = exception;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}

View File

@ -2,23 +2,27 @@ package com.github.pig.gateway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER;
/**
* @author lengleng
* @date 2017/11/20
* 在RateLimitPreFilter 之前执行不然又空指针问题
*/
@Component
public class AccessFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
return FORM_BODY_WRAPPER_FILTER_ORDER - 1;
}
@Override

View File

@ -0,0 +1,52 @@
package com.github.pig.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import com.github.pig.common.util.R;
import com.github.pig.gateway.service.LogSendService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ERROR_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER;
/**
* @author lengleng
* @date 2017-12-25 17:53:38
* 网关统一异常处理
*/
@Component
public class ErrorHandlerFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(ValidateCodeFilter.class);
@Autowired
private LogSendService logSendService;
@Override
public String filterType() {
return ERROR_TYPE;
}
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER + 1;
}
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
return requestContext.getThrowable() != null;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
logSendService.send(requestContext);
return null;
}
}

View File

@ -19,7 +19,6 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
*/
@Component
public class LoggerFilter extends ZuulFilter {
private static final String KEY_USER = "user";
@Autowired
private LogSendService logSendService;

View File

@ -9,7 +9,8 @@ import com.netflix.zuul.context.RequestContext;
public interface LogSendService {
/**
* 往消息通道发消息
* @param requestContext
*
* @param requestContext requestContext
*/
void send(RequestContext requestContext);
}

View File

@ -1,14 +1,18 @@
package com.github.pig.gateway.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.github.pig.common.constant.CommonConstant;
import com.github.pig.common.entity.SysLog;
import com.github.pig.common.util.UserUtils;
import com.github.pig.common.vo.ErrorPojo;
import com.github.pig.common.vo.LogVo;
import com.github.pig.gateway.service.LogSendService;
import com.netflix.zuul.context.RequestContext;
import com.xiaoleilu.hutool.http.HttpUtil;
import com.xiaoleilu.hutool.io.IoUtil;
import com.xiaoleilu.hutool.util.URLUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
@ -16,6 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author lengleng
@ -28,12 +36,20 @@ public class LogSendServiceImpl implements LogSendService {
@Autowired
private AmqpTemplate rabbitTemplate;
/**
* 1. 获取 requestContext 中的请求信息
* 2. 如果返回状态不是OK则获取返回信息中的错误信息
* 3. 发送到MQ
*
* @param requestContext 上下文对象
*/
@Override
public void send(RequestContext requestContext) {
HttpServletRequest request = requestContext.getRequest();
String requestUri = request.getRequestURI();
String method = request.getMethod();
SysLog log = new SysLog();
log.setType(CommonConstant.STATUS_NORMAL);
log.setRemoteAddr(HttpUtil.getClientIP(request));
log.setRequestUri(URLUtil.getPath(requestUri));
log.setMethod(method);
@ -42,16 +58,48 @@ public class LogSendServiceImpl implements LogSendService {
log.setCreateBy(UserUtils.getUserName(request));
Long startTime = (Long) requestContext.get("startTime");
log.setTime(System.currentTimeMillis() - startTime);
//正常发送服务异常解析
if (requestContext.getResponseStatusCode() != HttpStatus.SC_OK) {
InputStream inputStream = requestContext.getResponseDataStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream stream1 = null;
InputStream stream2 = null;
byte[] buffer = IoUtil.readBytes(inputStream);
try {
baos.write(buffer);
baos.flush();
stream1 = new ByteArrayInputStream(baos.toByteArray());
stream2 = new ByteArrayInputStream(baos.toByteArray());
String resp = IoUtil.read(stream1, CommonConstant.UTF8);
ErrorPojo error = JSONObject.parseObject(resp, ErrorPojo.class);
log.setType(CommonConstant.STATUS_LOCK);
log.setException(error.getMessage());
requestContext.setResponseDataStream(stream2);
} catch (IOException e) {
logger.error("响应流解析异常:", e);
throw new RuntimeException(e);
} finally {
IoUtil.close(stream1);
IoUtil.close(baos);
IoUtil.close(inputStream);
}
}
//网关内部异常
Throwable throwable = requestContext.getThrowable();
if (throwable != null) {
logger.error("网关异常", throwable);
log.setException(throwable.getMessage());
}
//保存发往MQ只保存授权
LogVo logVo = new LogVo();
logVo.setSysLog(log);
//解析用户名的事情异步到rabbit消费中处理
if (StringUtils.isNotEmpty(request.getHeader(CommonConstant.REQ_HEADER))) {
logVo.setToken(request.getHeader(CommonConstant.REQ_HEADER));
}
try {
rabbitTemplate.convertAndSend(CommonConstant.LOG_QUEUE, logVo);
}catch (Exception e) {
logger.error("MQ发送日志异常异常信息"+e.getMessage());
}
}
}