Vue-history模式的SpringBoot配置以及对错误状态的处理
使用history模式
vue-router 默认是hash模式的,但是连中文官网都承认hash 模式的url很丑,的确像我这种小渣渣都能一眼看出这是个单页面无疑了。比如这样http://xxx.com/#/home/index
官网提出了一种能让url能像http://xxx.com/home/index
这样显示的办法,就是利用HTML5的history属性
vue-router配置
配置方法很简单:
history模式:在实例Router的地方加入mode: 'history'
根路由:加入base: 'base'
(这么做是因为服务器只需要对base相关的url进行转发操作就可以了,同时也能对404画面进行处理)
那么我们刚才的路由正确访问方法应当时这样的:http://xxx.com/base/home/index
但是这个模式的缺点就是需要后台对这种连接做一些处理,否则get不到对应的资源只会返回404。
后台配置
我用SpringBoot框架随便定义了一个controller:
@RequestMapping(value={
"/base",
"/base/**"
})
public String fowardRouter(){
return "forward://index.html";
}
(@RequestMapping的value值可以是多个,以上是对根路由相关的url进行相应处理)
如果访问根路由下不存在的路由:http://xxx.com/base/notExists
,会调用fowardRouter(),将会出现空白页;
如果连根路由都没写:http://xxx.com/randomstr/notExists
,那么会由服务器返回404
以下是对vue-router单页面和springboot前后分离框架的异常状态处理
错误状态处理
在上面这种配置的情况下,找不到页面有可能有两种情况,一种是找不到的路由,一种就是服务器返回的404,找不到路由的情况我们还是得靠前台处理,但服务器其返回的所有异常状态,SpringBoot都会返回默认的error页面
针对不存在的路由的处理
使用vue-router是有可能发生用户随意修改路由地址,造成访问了不存在的路由出现空白页面的情况。结合官网对HTML5 history模式给出的警告,我们可以在配置路由时增加一个路由地址,覆盖所有的路由情况。
routes: [
{
path: '*',
component: ERROR404
},
...其他路由...
]
ERROR404
是我们应该要自定义的组件,渲染不存在的路由的画面
由服务器返回的异常状态的处理
上面说过SpringBoot会根据错误状态码返回指定的页面,网上也有很多资源关于如何在发生错误时指定到自定义模板上。不过我想处理的方式是所有的错误页面全都由单页面组件来完成,这样可能就需要不管是不是ajax请求我们都不能用服务器渲染的方式,只能用返回JSON数据的方式来统一处理。
下面是SpringBoot服务器对此状况的处理:
拦截错误状态请求,主动抛异常
首先第一步重写了SpringBoot对错误页面的默认处理,我们需要实现ErrorController:
@Controller
public class MainErrorController implements ErrorController {
private static final String ERROR_PATH = "/error";
@Override
public String getErrorPath() {
return ERROR_PATH;
}
@RequestMapping(ERROR_PATH)
public Object handleError(HttpServletRequest request)
{
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
switch (statusCode) {
case 404:
throw new NoHandlerFoundException(request.getMethod(), request.getRequestURI(), null);
case 401:
throw new UsernameNotFoundException(null);
case 500:
// 这是一个自定义的异常
throw new MyException();
default:
break;
}
System.out.println("其他错误啦"+statusCode);
return null;
}
}
我列举出了少数情况,但是都在各种异常状态下主动抛异常,其实我完全不用这么做的,如果用服务器渲染的方式,各自返回相应的模板url即可,比如你自定义了一个好看的404页面,你直接return "error404"
就结束了。
但我的工作还未停止。
我之所以没在这里直接返回JSON数据,是因为所需要返回的格式不一样。
例如404和500,针对于404我可能需要重定向到我的单页面的html:"forward://index.html"
但对于500,我只想返回json数据即可,比如{"errorStatus", 500}
,那么如果我都放在这个handleError方法下处理,就需要这个方法既能够@Response的方式处理,又能够以返回模板url的方式处理,我是不知道能不能实现,反正我实现不了。
处理全局异常
新建一个注解了@ControllerAdvice的类,就能够对Controller级别的异常进行拦截处理。下面是我对刚才各种状态主动抛的异常的处理。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UsernameNotFoundException.class)
@ResponseBody
Object handleException(){
Map<String,Object> ret = new HashedMap<>();
ret.put("errorStatus", 401);
return ret;
}
@ExceptionHandler(NoHandlerFoundException.class)
String handleException404(){
return "forward://index.html";
}
@ExceptionHandler(MyException.class)
@ResponseBody
Object handleException2(){
Map<String,Object> ret = new HashedMap<>();
ret.put("errorStatus", 500);
return ret;
}
}
我上面是对401状态的处理有点随意了,大家不要效仿,以后补全如何精确的处理认证失败状态。
只要在方法上注解ExceptionHandler并添加要处理的异常类,就可以对异常情况分门别类的进行处理了。
除了404是重新跳转回去以错误路由方式处理,以上大多处理方式都是返回JSON的方式"errorStatus", 500
,然后在前台用拦截器来处理:
vue-resource拦截器
所有由服务器返回的自定义状态,我们都可以用vue-resoursse的拦截器来处理
前提是我们已经引入了此模块import VueResource from 'vue-resource'
Vue.http.interceptors.push((request, next) => {
next((response) => {
let errorStatus = response.data.errorStatus
if (!errorStatus) {
return response
}
if (errorStatus === 401) {
if (request.url === '/login') {
store.state.logins = '401'
}
router.push('/login')
} else if (errorStatus === 500) {
store.state.lastRouter = router.currentRoute.fullPath
router.push('/error/syseeror500')
} else if (errorStatus === 404) {
store.state.lastRouter = router.currentRoute.fullPath
router.push('/error/syseeror404')
}
return response
})
})
/error/syseerorxxx
都是路由地址,指定了我自定义的组件
vue-resource的interceptors的push方法为我们提供一个在每次http请求响应后优先执行的函数,这个函数的第一个参数request即请求体,next是一个以response为参数的回调函数。
我的处理方式不好
- 我不知我是不是应该定义几个模板由服务器来渲染,可能会简单的多
- 如果非要以我的方式,有没有更简化的可能?
网上有说用重写各种异常状态的默认请求地址:
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(
ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"));
container.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400"));
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"));
container.addErrorPages(new ErrorPage(HttpStatus.BAD_GATEWAY, "/error/500"));
container.addErrorPages(new ErrorPage(Throwable.class, "/error/500"));
container.addErrorPages(new ErrorPage(Exception.class, "/error/500"));
}
};
}
然后我们只需在MainErrorController中增加对应的处理方法即可,我试了没好用,如果有清楚的朋友希望多多提意见。