당신 이 모 르 는 SpringBoot 와 Vue 배치 솔 루 션

머리말
얼마 전에 회사 의 외부 네트워크 에 배 치 된 프레젠테이션 환경 은 모두 내부 네트워크 환경 으로 옮 겨 갔 고 모든 대외 프레젠테이션 환경 은 외부 네트워크 맵 을 신청 해 야 특정한 서 비 스 를 방문 할 수 있 었 다.나 는 하나의 외부 네트워크 주소www.a.com로 내부 네트워크 주소http://ip:port를 매 핑 한 다음 에 이 주소http://ip:port에서 nginx 를 대리 로 각 그룹의 프로젝트http://ipn:portn에 전달 했다.그 중에서 도 정적 자원 404 를 만 났 는데 주로 이 404 문 제 를 해결 했다.
최근 에 또 하나의 프로젝트 를 만 들 었 습 니 다.사용자 의 체험 을 고려 하여 배치 의 복잡성 을 줄 이 고 SpringBoot 로 웹 서버 에 전단 자원 을 웹 자원 으로 매 핑 하 는 방법 을 생각 했 습 니 다.
조건 이 허용 되 거나 성능 에 대한 요구 가 높 습 니 다.전후 단 분리 배치,nginx 는 웹 서버 를 하고 백 엔 드 는 인터페이스 서비스 만 제공 하 는 것 을 추천 합 니 다.
이전에 배 치 된 프로젝트 A 의 외부 네트워크 방문 주 소 는http://ip1:8080이 고 외부 네트워크 가 비 친 후에 만 방문 할 수 있다http://ip/app1.이전 프로젝트 B 의 외부 네트워크 방문 주 소 는http://ip1:8081이 고 프로젝트 의 방문 주 소 는http://ip/app2이다.이것 도 크 지도 작 지도 않 은 변동 이 라 고 할 수 있 지만 전환 한 후에 발생 하 는 첫 번 째 문 제 는 바로 정적 자원 의 퍼 가기 로 인해404이다.
예 를 들 어 예전 프로젝트 A 의 방문 주 소 는http://ip1:8080문맥 이 없 었 습 니 다.
현재 A 의 방문 주 소 는http://ip/app1이 고 문맥 app 1 이 여기 있어 서 일부 자원 404 가 있 습 니 다.
예 를 들 어 원래http://ip1:8080index.html 자원 을 요 청 했 는데 지금 은http://ip/app1index.html 만 요청 할 수 있 습 니 다.

<!-- index.html -->
<!--          -->
<link href="/index.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet">
이전 방문index.css주 소 는http://ip1:8080/index.css이 었 으 나 지금 은 방문http://ip/index.css으로 바 뀌 어 404,실제 index.css 주 소 는http://ip/app1/index.css입 니 다.
전단 사용vue으로 작 성 됩 니 다.html 의 정적 자원 경 로 는 잘 해결 되 고 웹 팩 포장 을 수정 하면 됩 니 다.

<!--          -->
<link href="/index.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet">

<!--        -->
<link href="./index.css" rel="external nofollow" rel="stylesheet">

<!--    webpack           -->
<link href="<%= BASE_URL %>index.css" rel="external nofollow" rel="stylesheet">
그러나 프로젝트 중 일부 구성 요소 의 요청 은 통일 적 으로 처리 할 수 없고 코드 만 바 꿀 수 있 습 니 다.그러나 저 는 코드 를 움 직 이 고 싶 지 않 습 니 다.웹 팩 포장 은 움 직 이 고 싶 지 않 습 니 다.이러한 수 요 를 바탕 으로 해결 방법 을 생각 했 습 니 다.
본문 내용
  • Nginx 는 vue 프로젝트 를 배치 하고 정적 자원 의 분실 을 어떻게 우호 적 으로 처리 할 수 있 습 니까
  • SpringBoot 는 웹 서버 의 기능 맵 vue 프로젝트 를 웹 자원 으로 제공 하고 vue 경로 에서 index.html 문 제 를 전달 합 니 다.
  • 데모 코드 주소
    Nginx 배치 Vue 프로젝트
    
    server {
      listen 8087;
      #            ,        /app1   ,       /app1/ ,            /app1 。   ,   
      location / {
        try_files $uri $uri/;
      }
      root /Users/zhangpanqin/staic/;
      location ~ /(.*)/ {
        index index.html /index.html;
        try_files $uri $uri/ /$1/index.html;
      }
    }
    /Users/zhangpanqin/staic/배 치 된 프로젝트,예 를 들 어 app 의 프로젝트 자원 을/Users/zhangpanqin/staic/app아래 에 놓 습 니 다.방문 주소http://ip/8087/app
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <!--            BASE_URL    vue.config.js     publicPath-->
      <link rel="icon" href="<%= BASE_URL %>favicon.ico" rel="external nofollow" >
      <!--     ,     index.css -->
      <link href="/index.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet">
    </head>
    </html>
    브 라 우 저 에 vue 를 입력 할 수 있 는 경로/app/blog도 페이지 에 접근 할 수 있 도록vue-router의 base 속성 을 추가 해 야 합 니 다.
    
    import Vue from 'vue';
    import VueRouter from 'vue-router';
    
    Vue.use(VueRouter);
    
    const routes = [
      {
        path: '/',
        name: 'Home',
        component: () => import('@/views/Home.vue'),
      },
      {
        path: '/blog',
        name: 'Blog',
        component: () => import('@/views/Blog.vue'),
      },
      {
        //               
        path: '*',
        name: 'Error404',
        component: () => import('@/views/Error404.vue'),
      }
    ];
    const router = new VueRouter({
      //        ,     vue mode      。
      // https://cli.vuejs.org/zh/guide/mode-and-env.html
      // https://router.vuejs.org/zh/api/#base
      base: process.env.VUE_APP_DEPLOY_PATH,
      mode: 'history',
      routes,
    });
    
    export default router;
    http://localhost:8087/app/index.csscss 의 실제 주소 입 니 다.그래서/app로 시작 하지 않 는 자원 에/app을 더 하면 된다 고 생각 했 습 니 다.쿠키 만 할 수 있다 고 생각 했 습 니 다.x_vue_path각 항목 의 경 로 를 기록 한 다음 에 정적 자원 은 이 경로 에서 찾 습 니 다.$cookie_x_vue_path/$uri아래 설정 은 try 를 사 용 했 습 니 다.files 내부 리 셋 자원 은 브 라 우 저 에서 리 셋 되 지 않 습 니 다.
    
    # gzip ,     epoll       
    server {
      listen 8087;
      #            ,        /app1   ,       /app1/ ,            /app1 。   ,   
      location / {
        try_files $uri $uri/;
      }
      root /Users/zhangpanqin/staic/;
    
      # (.*)        ,    app1 app2  
      location ~ /(.*)/.*/ {
        index index.html /index.html;
        add_header Set-Cookie "x_vue_path=/$1;path=/;";
        # /Users/zhangpanqin/staic/+/$1/index.html          index.html
        try_files $uri $uri/ /$1/index.html @404router;
      }
      #       ,          。
      location ~ (.css|js)$ {
        try_files $uri $cookie_x_vue_path/$uri @404router;
      }
      location @404router {
        return 404;
      }
    }

    다음은 이것 은 방향 을 바 꾸 는 설정 입 니 다.
    
    server {
      listen 8087;
      root /Users/zhangpanqin/staic/;
    
      location ~ /(.*)/.*/? {
        index index.html /index.html;
        add_header Set-Cookie "x_vue_path=/$1;path=/;";
        try_files $uri $uri/ /$1/index.html @404router;
      }
      location ~ (.css|js)$ {
        #     /app/index.css    ,    
        rewrite ^($cookie_x_vue_path)/.* $uri break;
        #       /index.css 302        /app/index.css
        rewrite (.css|js)$ $cookie_x_vue_path$uri redirect;
      }
      location @404router {
        return 404;
      }
    }

    이 사고방식 에 따라 모든 자원 을 전송 할 수 있다.업무 코드 를 바 꾸 지 않 고vue-routerbase기초 루트 를 하나 더 추가 하면 된다.
    SpringBoot 배치 Vue 프로젝트Nginx통 했 습 니 다.SpringBoot 는 그대로 바 가 지 를 그리 면 됩 니 다.자바 가 편 하 게 쓸 수 있 습 니 다.debug,하하.
    SpringBoot 맵 정적 자원
    
    @Configuration
    public class VueWebConfig implements WebMvcConfigurer {
      /**
       *          
       * file:./static/        user.dir   ,jar         static
       */
      private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {"file:./static/", "classpath:/META-INF/resources/",
          "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
    
      @Override
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //         
        CacheControl cacheControl = CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic();
        registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS).setCacheControl(cacheControl);
      }
    
    
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
        //         ,        cookie 
        registry.addInterceptor(new VueCookieInterceptor()).addPathPatterns("/test/**");
      }
    
      // vue        ,           
      @Bean
      public VueErrorController vueErrorController() {
        return new VueErrorController(new DefaultErrorAttributes());
      }
    }
    프로젝트 정적 자원 경로 에 쿠키 추가
    
    public class VueCookieInterceptor implements HandlerInterceptor {
      public static final String VUE_HTML_COOKIE_NAME = "x_vue_path";
    
      public static final String VUE_HTML_COOKIE_VALUE = "/test";
    
      /**
       *          /test       cookie
       */
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        final Cookie cookieByName = getCookieByName(request, VUE_HTML_COOKIE_NAME);
        if (Objects.isNull(cookieByName)) {
          final Cookie cookie = new Cookie(VUE_HTML_COOKIE_NAME, VUE_HTML_COOKIE_VALUE);
          //      url      
          cookie.setPath("/");
          cookie.setHttpOnly(true);
          response.addCookie(cookie);
        }
        return true;
      }
    
      public static Cookie getCookieByName(HttpServletRequest httpServletRequest, String cookieName) {
        final Cookie[] cookies = httpServletRequest.getCookies();
        if (Objects.isNull(cookieName) || Objects.isNull(cookies)) {
          return null;
        }
        for (Cookie cookie : cookies) {
          final String name = cookie.getName();
          if (Objects.equals(cookieName, name)) {
            return cookie;
          }
        }
        return null;
      }
    }
    자원 전송 오류 요청
    잘못된 점프 에 접근 하려 면 인터페이스 요청 과 정적 자원 의 요청 을 구분 하고 accept 를 통 해 판단 할 수 있 습 니 다.
    
    @RequestMapping("/error")
    public class VueErrorController extends AbstractErrorController {
    
      private static final String ONLINE_SAIL = VUE_HTML_COOKIE_NAME;
    
      private static final String ERROR_BEFORE_PATH = "javax.servlet.error.request_uri";
    
      public VueErrorController(DefaultErrorAttributes defaultErrorAttributes) {
        super(defaultErrorAttributes);
      }
    
      @Override
      public String getErrorPath() {
        return "/error";
      }
    
      @RequestMapping
      public ModelAndView errorHtml(HttpServletRequest httpServletRequest, HttpServletResponse response, @CookieValue(name = ONLINE_SAIL, required = false, defaultValue = "") String cookie) {
        final Object attribute = httpServletRequest.getAttribute(ERROR_BEFORE_PATH);
        if (cookie.length() > 0 && Objects.nonNull(attribute)) {
          response.setStatus(HttpStatus.OK.value());
          String requestURI = attribute.toString();
          //          vue        ,          
          if (!requestURI.startsWith(cookie)) {
            ModelAndView modelAndView = new ModelAndView();
            modelAndView.setStatus(HttpStatus.OK);
            //         ,     ,    redirect
            String viewName = "forward:" + cookie + requestURI;
            modelAndView.setViewName(viewName);
            return modelAndView;
          }
        }
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setStatus(HttpStatus.OK);
        modelAndView.setViewName("forward:/test/index.html");
        return modelAndView;
      }
    
      //        accept   application/json    ,        json   
      @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
      public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
          return new ResponseEntity<>(status);
        }
        final Map<String, Object> errorAttributes = getErrorAttributes(request, true);
        return new ResponseEntity<>(errorAttributes, status);
      }
    홈 페이지 이동
    
    @Controller
    public class IndexController {
      @RequestMapping(value = {"/test", "/test"})
      public String index() {
        return "forward:/test/index.html";
      }
    }
    본 고 는 장 클 라 이 밍 의 블 로그 www.mfly you.cn/가 창작 한다.자 유 롭 게 옮 겨 싣 고 인용 할 수 있 으 나 서명 한 작가 가 글 의 출처 를 밝 혀 야 한다.
    당신 이 모 르 는 SpringBoot 와 Vue 배치 솔 루 션 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 SpringBoot 와 Vue 배치 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기