背景

目前我所使用的打包方式:

  1. 首先通过 npm run build命令将前端页面打包成静态资源
  2. 将静态资源复制到SpringBoot项目的 /resources/static 目录下
  3. 编写错误页面配置类,统一重定向到/index.html

问题

  1. 按照如上方式操作后,只能通过http://ip:port/index.html访问,然后通过点击按钮/菜单的形式实现路由跳转。
  2. 成功打包并部署系统后,用户访问URL收藏页面,下次直接点击收藏的页面会出现一片空白

原因: 用户访问/index.html后,Vue Router会跳转到根路由,此时用户收藏当前页面的URL可能为http://ip:port/login,导致下一次访问页面出现空白

解决

后端配置

自定义 GlobalErrorController

具体原因请参考 https://blog.csdn.net/qq_29684305/article/details/82286469 以及 ErrorMvcAutoConfigurationBasicErrorController源码
重写后的代码如下:

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class GlobalErrorController extends AbstractErrorController {

    private static final Logger log = LoggerFactory.getLogger(GlobalErrorController.class);

    private final ErrorProperties errorProperties;

    public GlobalErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) {
        super(errorAttributes);
        errorProperties = serverProperties.getError();
    }

    @Override
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }
    
    /**
     * 通过网页直接访问,出现404错误时会执行此方法,此处将页面重定向到index.html,并添加真实的路由地址参数交由前台处理
     */
    @RequestMapping(
            produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(HttpStatus.FOUND.value());
        String routePath = ((String)model.get("path"));
        try {
            response.sendRedirect("/index.html?route="+routePath);
        } catch (IOException e) {
            log.error("重定向到首页出错,错误原因: {}",e.getMessage());
        }
        return null;
    }
    

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = this.getStatus(request);
        return new ResponseEntity(body, status);
    }

    private boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        ErrorProperties.IncludeStacktrace include = this.getErrorProperties().getIncludeStacktrace();
        if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
            return true;
        } else {
            return include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM ? this.getTraceParameter(request) : false;
        }
    }

    private ErrorProperties getErrorProperties() {
        return this.errorProperties;
    }
}

前端配置

思路来源: https://segmentfault.com/q/1010000015880077

router.afterEach((to, form) => {
  // 添加到后置导航守卫是因为跳转index.html后Vue / Vue Router 实例化完成后路由才会被挂载,如果放在前置导航首位则路由跳转不成功
  if (to.path === "/index.html") {
    // 获取真实路由参数
    var routePath = getQueryVariable("route");
    if (routePath) {
      // URI解码(将%2F解码为/)
      routePath = decodeURIComponent(routePath);
      // 如果默认跳转到首页,则不做二次路由跳转
      if (routePath !== '/index.html') {
        router.replace(routePath);
      }
    }
  }

  NProgress.done()
})

通过如上配置后,再次打包,浏览器中直接输入路由地址会改变两次,示例如下:

  1. 访问http://ip:port/demo
  2. 后端重定向到/index.html并附带路由地址,此时地址栏url变为http://ip:port/index.html?route=%2Fdemo (/编码后会变成%2F)
  3. 后置导航守卫通过javascript操作地址栏实现真实路由的跳转:http://ip:port/demo

2020-05-07补充

  • 此方法暂时存在问题,如果在开发环境下访问index.html页面会出现无限重定向导致浏览器崩溃问题,暂无解决方案。
Logo

前往低代码交流专区

更多推荐