随着UI 技术的发展,react 等技术的流行,越来越多的SPA(single page application),也就是单页面应用。js 的变化引起html dom 的变化,从而展现出各种变化,也就是页面的操作只是引起了dom 的变化,从头到尾就只有一个html。但是UI 中有各种 router 对应各种功能或者 “页面”。这就会导致浏览器中URL 会随着操作发生变化,这些URL很有可能后端服务器没有相应的API,如果在浏览器刷新就会出现错误,这是UI 开发就要求如果URL 不存在,就返回index 页面或者固定页面。
spring 版本:spring boot 2
默认处理方式
spring boot 在web 请求发生异常的时候会自动把请求转到 /error,spring boot 内置了一个BasicErrorController 对异常进行统一的处理
BasicErrorController源码
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties) {
this(errorAttributes, errorProperties, Collections.emptyList());
}
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
protected boolean isIncludeStackTrace(HttpServletRequest request,
MediaType produces) {
IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
if (include == IncludeStacktrace.ALWAYS) {
return true;
}
if (include == IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}
从代码可以看出spring boot 对于json 和html 格式的请求做了不同的处理。html 格式的请求可以返回自定义的view,在resolveErrorView中会寻找不同的模板。
DefaultErrorViewResolver 源码
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
//默认SpringBoot可以去找到一个页面? error/404
String errorViewName = "error/" + viewName;
//模板引擎可以解析这个页面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
if (provider != null) {
//模板引擎可用的情况下返回到errorViewName指定的视图地址
return new ModelAndView(errorViewName, model);
}
//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
return resolveResource(errorViewName, model);
}
会寻找error 目录下的模板,如果不存在显示默认的view。
解决方案
覆盖默认方式,继承BasicErrorController,在errorhtml 中如果是404,就返回到index
not found 源码
/**
* @description: ㄟ(▔, ▔)ㄏ
* @author: pangtoutuo
* @create: 2019-02-26
**/
@Controller
public class PageNotFoundController extends BasicErrorController {
@Value("${spring.web.indexview.path}")
private String indexViewPath;
@Autowired
private IndexView indexView;
public PageNotFoundController
(ErrorAttributes errorAttributes, ServerProperties serverProperties) {
super(errorAttributes, serverProperties.getError());
}
@Override
public ModelAndView errorHtml(
final HttpServletRequest request, final HttpServletResponse response
) {
HttpStatus status = getStatus(request);
if (HttpStatus.NOT_FOUND.equals(status)) {
return new ModelAndView(indexViewPath, indexView.getIndexModel());
}
return super.errorHtml(request, response);
}
}
这样一来json 格式错误按照默认方式返回错误。后端服务之间如果404 还是会异常,不影响后端业务。
网友评论