SpringBoot 整合 Vue打包,访问前端路由直接跳转到对应页面的方法

5,771 阅读2分钟

背景

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

  1. 首先通过 npm run build命令将前端页面打包成静态资源
  2. 将静态资源复制到SpringBoot项目的 /resources/static 目录下

问题

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

原因: 用户访问URL后,Vue-Router会跳转到首页(/index)或登录页面(/login),此时用户收藏当前页面的URL为http://ip:port/login,导致无法正常访问

解决

后端配置

自定义 GlobalErrorController

具体原因请参考 blog.csdn.net/qq_29684305… 以及 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;
    }
}

前端配置

思路来源: segmentfault.com/q/101000001…

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

  NProgress.done()
})