Preface
Some time ago, the demonstration environment of the company's external network deployment was all transferred to the Intranet environment , All external demonstration environments need to apply for external network mapping to access a certain service . I use an Internet address www.a.com
Mapping to an intranet address http://ip:port
, And then at this address http://ip:port
use nginx Acting as a proxy to forward items to groups http://ipn:portn
Up , There are also some static resources 404, The main thing is to solve this problem 404 problem .
I've done another project recently , Considering the user experience , Reduce the complexity of deployment , I thought of a way to use SpringBoot do web Server mapping front-end resources are web resources .
<font color=red> Conditions permit or require high performance , It is recommended that the front and rear end be deployed separately ,nginx do web The server , The back end only provides interface services </font>
Previously deployed projects A The Internet access address is http://ip1:8080
, After mapping, you can only access http://ip/app1
, Previous projects B The Internet access address is http://ip1:8081
, The project address is http://ip/app2
. This is not a small change , But the first problem after switching is that static resource forwarding leads to 404
.
For example, previous projects A The address is http://ip1:8080
It has no context .
And now A The address of is http://ip/app1
, There is a context app1 ad locum , It leads to some resources 404.
for instance : original http://ip1:8080
Request arrived. index.html resources , Now we can only http://ip/app1
Ask to index.html.
<!-- index.html -->
<!-- The original deployment environment is written in -->
<link href="/index.css" rel="stylesheet">
Previously visited index.css
The address is http://ip1:8080/index.css
, But now it's an interview http://ip/index.css
Lead to 404, actual index.css The address is http://ip/app1/index.css
Front end use vue
To write ,html The static resource path in can be well solved , modify webpack Just pack it .
<!-- The original deployment environment is written in -->
<link href="/index.css" rel="stylesheet">
<!-- Write it as a relative path -->
<link href="./index.css" rel="stylesheet">
<!-- combination webpack Path supplement when packing -->
<link href="<%= BASE_URL %>index.css" rel="stylesheet">
However, there are some component requests in the project that cannot be handled uniformly , You can only change the code . But I don't want to move code ,webpack I don't want to move the package , Based on these needs, I think of a way to solve .
The content of this article is
- Nginx Deploy vue project , How to handle the loss of static resources in a friendly way
- SpringBoot Provide web Function mapping of the server vue The project is web resources , And deal with vue Routing and forwarding index.html problem .
https://github.com/zhangpanqin/vue-springboot
Nginx Deploy Vue project
server {
listen 8087;
# Its function is not to redirect addresses , For example, browser input /app1 visit , You can also visit /app1/ , And the browser address doesn't change /app1 . Can't , Obsessive compulsive disorder
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/
Put deployed projects , such as app Project resources are put into /Users/zhangpanqin/staic/app
Next . The visiting address is http://ip/8087/app
<!DOCTYPE html>
<html lang="en">
<head>
<!-- It can also be changed to a similar address BASE_URL be equal to vue.config.js Configured publicPath-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- After deployment , Can't access index.css -->
<link href="/index.css" rel="stylesheet">
</head>
</html>
In order to be able to enter vue The routing /app/blog
You can also visit the page , Need to add vue-router
Medium base attribute .
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'),
},
{
// Jump here when the route cannot be matched
path: '*',
name: 'Error404',
component: () => import('@/views/Error404.vue'),
}
];
const router = new VueRouter({
// It's mainly to modify here , According to vue mode Environment .
// 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;
<img src="http://oss.mflyyou.cn/blog/20200727234702.png?author=zhangpanqin" alt="image-20200727234702928" style="zoom: 25%;" />
http://localhost:8087/app/index.css
by css The real address of . So try to find a way not to /app
Resources at the beginning plus /app
That's all right. , After thinking about it, only cookie Can do .
x_vue_path
Record the path of each project , Then static resources go to this path to find ,$cookie_x_vue_path/$uri
The following configuration uses try_files Internal redirection resources , There will be no redirection on on the browser side .
# gzip , cache and epoll None of the optimized ones have been written
server {
listen 8087;
# Its function is not to redirect addresses , For example, browser input /app1 visit , You can also visit /app1/ , And the browser address doesn't change /app1 . Can't , Obsessive compulsive disorder
location / {
try_files $uri $uri/;
}
root /Users/zhangpanqin/staic/;
# (.*) Which item is the match , for instance app1 app2 etc.
location ~ /(.*)/.*/ {
index index.html /index.html;
add_header Set-Cookie "x_vue_path=/$1;path=/;";
# /Users/zhangpanqin/staic/+/$1/index.html You can go to each project index.html
try_files $uri $uri/ /$1/index.html @404router;
}
# Find static resources , You can also add a cache here .
location ~ (.css|js)$ {
try_files $uri $cookie_x_vue_path/$uri @404router;
}
location @404router {
return 404;
}
}
Here is the redirection configuration
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)$ {
# Match to /app/index.css Resources for , Direct access
rewrite ^($cookie_x_vue_path)/.* $uri break;
# Resources accessed /index.css 302 Temporarily redirect to /app/index.css
rewrite (.css|js)$ $cookie_x_vue_path$uri redirect;
}
location @404router {
return 404;
}
}
According to this idea, all resources can be forwarded , Don't change the business code , Just give vue-router
Add a base
Basic routing .
SpringBoot Deploy Vue project
Nginx
It's working ,SpringBoot Just follow the gourd and draw the gourd , still java It's comfortable to write , can debug, ha-ha .
SpringBoot Mapping static resources
@Configuration
public class VueWebConfig implements WebMvcConfigurer {
/**
* Mapped static resource path
* file:./static/ Path is relative to user.dir route ,jar Package under the same level directory 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) {
// Add static resource cache
CacheControl cacheControl = CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic();
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS).setCacheControl(cacheControl);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Configure the resources to intercept , It is mainly used for add to cookie
registry.addInterceptor(new VueCookieInterceptor()).addPathPatterns("/test/**");
}
// vue Routing forwarding uses , Also do Interface request not found
@Bean
public VueErrorController vueErrorController() {
return new VueErrorController(new DefaultErrorAttributes());
}
}
Project static resource path add cookie
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";
/**
* Configure the request resource path /test Add all the following 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);
// Under the project of url You can take it with you
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;
}
}
Request error to forward resources
The wrong jump should be clearly distinguished Interface requests and static resource requests , adopt accept Can be judged .
@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();
// The access path is not to vue End of deployment path , Add path forwarding to access
if (!requestURI.startsWith(cookie)) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setStatus(HttpStatus.OK);
// Static resources don't want to forward , Redirect words , It is amended as follows 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;
}
// The processing request header is accept by application/json Request , The interface request returns json data
@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);
}
Home page Jump
@Controller
public class IndexController {
@RequestMapping(value = {"/test", "/test"})
public String index() {
return "forward:/test/index.html";
}
}
This paper is written by Zhang Panqin's blog http://www.mflyyou.cn/ A literary creation . Reprint freely 、 quote , But you need to sign the author and indicate the source of the article .
For example, the official account of WeChat , Please add the author's official account number 2 . WeChat official account name :Mflyyou