场景:前后的分离项目,前端vue框架,打包后放在Tomcat里访问,端口是8080,后端服务端口8058。访问前端项目时,调用后端接口报跨域。

后端环境

正常访问端口8058



经过nginx配置(文末具体展示)后,去除端口,如下:

前端开发环境

1. 配置开发和生产的环境变量

.env.development文件

# API服务路径
VITE_APP_BASE_URL = ""

.env.production文件

# API服务路径,注意没有端口号,是经过nginx处理后的后端服务地址
VITE_APP_BASE_URL = "http://192.168.1.4/"

2. vite.config.ts 文件配置开发代理

import { warpperEnv } from "./build";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite"; /** 当前执行node命令时文件夹的地址(工作目录) */
const root: string = process.cwd(); export default ({ command, mode }: ConfigEnv): UserConfigExport => {
const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH } =
warpperEnv(loadEnv(mode, root));
return {
base: VITE_PUBLIC_PATH,
root,
resolve: {
alias
},
// 服务端渲染
server: {
// 是否开启 https
https: false,
// 端口号
port: VITE_PORT,
host: "0.0.0.0",
// 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
proxy: {
"^/api": {
target: "http://192.168.1.4:8058",
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, "")
}
}
},
// 后面省略...
}
};

3. http 文件封装 axios,使用环境变量配置的基础API路径

// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
// 请求地址
baseURL: import.meta.env.VITE_APP_BASE_URL,
// 请求超时时间
timeout: 5000,
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
};

4. api 文件里使用示例

import { http } from "@/utils/http";

export type UserResult = {
success: boolean;
data: {
username: string;
roles: Array<string>;
accessToken: string;
refreshToken: string;
expires: Date;
};
}; /** 登录 */
export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/api/login", { data });
};

5. 开发环境项目预览

由于使用了代理,所以不会有跨域的问题,如图:

前端生产环境

生产环境采用Tomcat,前端项目打包好后,放在Tomcat的 webapps/ROOT 目录下即可,如图:



启动Tomcat,双击 apache-tomcat-8.5.93/bin/ 目录下的 startup.bat,linux机器到 bin 目录下,运行 ./startup.sh



访问,Tomcat默认端口为8080,访问后台接口报跨域错误



nginx配置后,去除端口,访问正常

nginx配置

以上可以看出,nginx配置的目的,就是去除前后端的端口差异,从而解决跨域的问题。配置文件 nginx/conf/nginx.conf 修改如下:

server {
listen 80;
# IP/域名都可以
server_name http://192.168.1.4;
#charset koi8-r;
#access_log logs/host.access.log main; location / {
root html;
index index.html index.htm;
proxy_pass http://192.168.1.4:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5;
} location ^~ /api/ {
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,Authorization,Accept,Origin,Keep-Alive,User-Agent,X-Mx-ReqToken,X-Data-Type,X-Auth-Token,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://192.168.1.4:8058/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5;
}
}

疑问解答

1. 此示例项目,本地模拟的线上环境,操作系统 windows,后端服务 nodejs。换成 linux 系统或其他后端服务(如:SpringBoot 等)还适用吗,为什么?

答:同样适用。

因为跨域问题是浏览器为了安全保证,当你前端服务访问跨域资源时,被浏览器默认禁止了,所以和后端服务是什么,没有必然关系;所以解决问题的思路也是让我们的访问请求,达到浏览器的要求,即不跨域(同一域名/IP、端口、协议),那当然使用 nginx 就可以。

2. 前端请求里的代理里的 ^api,和nginx配置里的 ^~ /api/ 有什么关系?

答:有关系,但是不是你想象的关系。

首先我们要确认,你的实际的请求地址里,其实不包含 api 这段路径。如图:



开发环境加这个只是为了代理区分,因为你可能要请求多个不同域名的后端服务:我可以在 vite.config.ts 文件的 proxy 里再加一个以 ^/auth 开头的,导向其他域名的后端服务。

其次代码里也可以看到 rewrite: path => path.replace(/^\/api/, ""),所以实际的请求地址,最终是去掉了 api

那为什么nginx里要配置成 api 呢?

因为线上环境是没有运行代理的,也就是说 rewrite: path => path.replace(/^\/api/, "") 这一段是没有生效的,那你前端实际的请求地址就是 http://192.168.1.4/api/login,而真正的地址里没有 api 这一段,所以多出来的这一段,正好让nginx去代理,^~ /api/ 变成 http://192.168.1.4:8058/,从而使请求地址变成 http://192.168.1.4:8058/login

3. 接上,这两 api 虽然不是我想象的关系,但是事实上必须保持一致,有没有什么可以解耦的办法?

答:有。

nginx一般不会去随便改动,所以我们的解决办法优先放在前端,让前端去适配nginx。

参考 vite.config.ts 文件的代理地址替换,那我们在实际请求地址时,也可以做一个替换处理:在 http 文件里的请求拦截器中,判断是否生产环境,如果是,则替换 ^api 为nginx配置的开头路径即可。

4. 接上,vite.config.ts 可以配置多个代理,nginx也可以配置多个后端服务,但是 http 文件里的 axios 封装只有一个 baseURL

答:不是有拦截器吗。

env 环境配置里增加多个域名字段,假如以 auth 开头是另一个域名,那就在拦截器里使用条件判断,如果是 auth 开头,修改 config.baseURL 地址。

或者封装多个 axois?不建议,代码重复率又高了。

5. 既然nginx已经把 api 开头的都代理到了后端服务,那我前端就不能有 api 开头的路由?

答:可以。

看看访问路径,前端是哈希路由,有 # 号分隔的。如果是 history 路由呢,有试过的同学可以在评论区吱个声。

nginx配置解决跨域访问的更多相关文章

  1. 巧用Nginx配置解决跨域问题

    页面nginx配置 1,前端页面放在域名根目录,比如,http://www.xuecheng.com/ ,对应的nginx配置: #门户 location / { alias D:/Z_lhy/Spr ...

  2. nginx 配置ajax跨域访问php接口

    在nginx.conf里面,找到server项,并在里面添加如下配置 location ~ \.php?($|/) { #try_files $uri =; #handel cosr by mao a ...

  3. Nginx 代理解决跨域问题分析

    Nginx 代理解决跨域问题分析   当你遇到跨域问题,不要立刻就选择复制去尝试.请详细看完这篇文章再处理 .我相信它能帮到你. 分析前准备: 前端网站地址:http://localhost:8080 ...

  4. 使用Nginx来解决跨域的问题

    使用Nginx来解决跨域的问题 nginx的版本:(查看nginx命令: /usr/local/nginx/sbin/nginx -v) nginx/1.4.3 问题是:前端项目域名是 a.xxxx. ...

  5. 【Nginx】使用Nginx如何解决跨域问题?看完这篇原来很简单!!

    写在前面 当今互联网行业,大部分Web项目基本都是采用的前后端分离模式.前端为H5项目,后端为Java.PHP.Python等项目.而且大部分后端服务并不会只部署一套服务,而是会采用Nginx对后端服 ...

  6. springboot 前后端分离开发解决跨域访问

    最近新学习了Java EE开发框架springboot,我在使用springboot前后台分离开发的过程中遇到了跨域求问题.在网上寻找答案的过程中发现网上的解决方案大多比较零散,我在这里整理一个解决方 ...

  7. java 解决nginx代理的跨域访问问题

    一.什么是跨域 跨域是浏览器对JavaScript同源策略的限制 二.什么情况下会产生跨域 域名不同 wwww.baidu.com www.jd.com 域名相同,访问的端口不同 wwww.baidu ...

  8. 一篇文章让你搞懂如何通过Nginx来解决跨域问题

    Nginx跨域实现   首先大家要搞清楚什么是跨域,为什么会有跨域情况的出现.哪些情况属于跨域? 跨域:由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容 注:同源策略,单说来就是 ...

  9. Axiso解决跨域访问(...XMLHttpRequest cannot load http://xxx.xxx No 'Access-Control-Allow-Origin'...)

    直接访问如下:this.$axios.get("http://localhost:8089/yc/demo").then(res=>{    console.log(res) ...

  10. Axiso解决跨域访问

    问题: 在项目中需要需要讲本地项目去请求一个URL接口获取数据 例如: 本地请求地址:http://127.0.0.1:19323/site/info.json 请求Url地址:http://www. ...

随机推荐

  1. 如何使用Java在Excel中实现一个数据透视表

    摘要:本文由葡萄城技术团队于博客园原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 前一段时间淘宝出了一个"淘宝人生"的模块, ...

  2. 【调制解调】SSB 单边带调幅

    说明 学习数字信号处理算法时整理的学习笔记.同系列文章目录可见 <DSP 学习之路>目录,代码已上传到 Github - ModulationAndDemodulation.本篇介绍 SS ...

  3. KVM "shutting down, reason=crashed" 问题处理

    打开debug日志抓取信息 2022-10-12 07:42:43.698+0000: 63115: debug : processMonitorEOFEvent:4814 : Monitor con ...

  4. 【go语言】2.2.1 数组和切片

    数组和切片是 Go 语言中常用的数据结构,它们都可以存储多个同类型的元素. 数组 数组是具有固定长度的数据类型,它的长度在定义时就已经确定,不能随意改变. 你可以使用以下方式定义一个数组: var a ...

  5. Windows 环境下载、安装、使用(.Net 5.0) Redis 数据库及常见问题的解决

    〇.前言 Redis (Remote Dictionary Server 远程字典服务)是一个使用 ANSI C 编写的开源.包含多种数据结构,支持网络.基于内存.可选持久性的键值对存储数据库,是现在 ...

  6. 在不修改代码情况下解决 Chrome 浏览器跨域

    前言: 在前后台分离的项目,跨域是经常遇到的问题了.是实际解决方案中,大部分采用服务端配置,而如果只是调试,可以通过配置 Chrome 浏览器实现跨域,以下以 NodeJs 服务为例. 开始: 1. ...

  7. nginx反向代理常用基本配置

    http反向代理 http { ...     server {     listen 80;         location / {         proxy_pass http://192.1 ...

  8. 【Unity3D】平面光罩特效

    1 前言 ​ 屏幕深度和法线纹理简介中对深度和法线纹理的来源.使用及推导过程进行了讲解,激光雷达特效中讲述了一种重构屏幕像素点世界坐标的方法,本文将沿用激光雷达特效中重构像素点世界坐标的方法,实现平面 ...

  9. Ansible快速复习

    本文章是上一篇ansible自动化运维的快速复习以及项目上用到的一些命令,针对长时间未使用ansible,导致命令忘记的同学,帮助快速回忆.详细内容还是看上一章'ansible自动化运维'. 查看版本 ...

  10. Hugging News #0821: Hugging Face 完成 2.35 亿美元 D 轮融资

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...