Vue-history模式的SpringBoot配置以及对错误状态的处理

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中增加对应的处理方法即可,我试了没好用,如果有清楚的朋友希望多多提意见。

猜你喜欢

转载自blog.csdn.net/weixin_39080782/article/details/79639775
今日推荐