vip.justlive.common.web.base.CodedExceptionResolver Maven / Gradle / Ivy
/*
* Copyright (C) 2018 justlive1
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package vip.justlive.common.web.base;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import com.google.common.collect.Maps;
import vip.justlive.common.base.constant.BaseConstants;
import vip.justlive.common.base.exception.CodedException;
import vip.justlive.common.base.exception.NoStackException;
/**
* CodedException统一处理
*
* 对所有未处理的CodedException类型的异常进行统一的包装处理
*
* - 对于jsp形式的调用 在Model中放入一个error的对象
* - 对于ajax方式的请求 以请求希望的格式(JSON或者XML)方式返回error数据
*
*
* @author wubo
*/
@Component("codedExceptionResolver")
public class CodedExceptionResolver extends SimpleMappingExceptionResolver {
private static final String APPLICATION_ALL = "application/*";
private static final String HEAD_X_REQUESTED_WITH = "X-Requested-With";
private static final String HEAD_XMLHTTPREQUEST = "XMLHttpRequest";
private MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
@Value("${exception.errorPage:/error/coded_error}")
private String errorPage;
@PostConstruct
void init() {
Properties exceptionMappings = new Properties();
// 在非ajax方式的情况下,将所有异常转向指定的错误页面
exceptionMappings.put(Exception.class.getName(), errorPage);
this.setExceptionMappings(exceptionMappings);
}
@Override
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// 检查应用层是否对异常进行了处理
String viewName = determineViewName(ex, request);
if (viewName == null) {
return null;
}
// 包装Response对象
final Map error;
// 确定当前异常是业务异常还是系统故障。为了防止信息丢失,除了明确使用NoStackException,其余的异常都认为是系统故障
boolean isFault = true;
if (CodedException.class.isInstance(ex)) {
error = buildError((CodedException) ex);
if (NoStackException.class.isInstance(ex)) {
isFault = false;
}
} else {
error = buildError(ex);
}
if (isFault) {
// 记录日志 TODO
}
// 判断请求是否支持json格式数据返回
if (!isJsonResponseSupported(request)) {
Integer statusCode = determineStatusCode(request, viewName);
if (statusCode != null) {
applyStatusCodeIfPossible(request, response, statusCode);
}
ModelAndView model = getModelAndView(viewName, ex, request);
model.addObject("error", error);
return model;
} else {// JSON格式返回
return new ModelAndView(jsonView, error);
}
}
/**
* 判断一个请求是否支持Json方式的返回
*
* @param request 请求
* @return true是支持json方式返回
*/
protected boolean isJsonResponseSupported(HttpServletRequest request) {
// 检查是否有Accept头
final String accept = request.getHeader(HttpHeaders.ACCEPT);
// 对于没有定义Accept头的非标准HTTP协议请求,缺省认为客户端支持json数据
if (accept == null) {
return true;
}
// 当客户端是通过ajax方式发送的请求,则缺省认为支持json返回
final String ajax = request.getHeader(HEAD_X_REQUESTED_WITH);
if (ajax != null && ajax.indexOf(HEAD_XMLHTTPREQUEST) >= 0) {
return true;
}
// 客户端声明支持JSon
return accept.indexOf(MediaType.ALL_VALUE) >= 0 || accept.indexOf(APPLICATION_ALL) >= 0
|| accept.indexOf(MediaType.APPLICATION_JSON_VALUE) >= 0;
}
/**
* 根据CodedException创建返回对象
*
* @param ex 包装异常
* @return 返回对象
*/
private Map buildError(CodedException ex) {
Map err = Maps.newHashMap();
err.put(BaseConstants.RESP_CODE_FIELD, ex.getErrorCode().toString());
err.put(BaseConstants.RESP_MESSAGE_FIELD, ex.getMessage());
err.put(BaseConstants.RESP_IS_SUCCESS, false);
return err;
}
/**
* 包装普通的Exception
*
* @param ex 异常
* @return 返回对象
*/
private Map buildError(Exception ex) {
Map err = Maps.newHashMap();
err.put(BaseConstants.RESP_CODE_FIELD, ex.getClass().getSimpleName());
err.put(BaseConstants.RESP_MESSAGE_FIELD, ex.getMessage());
err.put(BaseConstants.RESP_IS_SUCCESS, false);
return err;
}
}