在微服务中,通常会使用前后端分离的方式进行开发和部署。由于前后端分开部署,属于不同的“资源”,因此前端调用后端API时可能会出现跨域问题,Cross-Origin Resource Sharing (CORS)。

这里,我们使用前后端分离的架构,使用nginx分别代理前端和后端微服务,分析和解决跨域问题。

前后端信息

微服务访问地址:

后端地址:http://192.168.122.130:8089(真实地址)

NGINX代理地址:

前端VIP:http://192.168.122.130(对外接口)

后端VIP:http://192.168.122.130:8080(对外接口)

前端服务

前端使用vue.js框架

//HelloWorld.vue<template><div class="hello"><p>后端地址:<input v-model="url"></p><p>返回结果:{{ message }}</p><div><a @click="getMethod">GET请求</a></div><div><a @click="getMethodWithHeader">带自定义请求头的GET请求</a></div><div><a @click="postMethod">POST请求</a></div><div><a @click="putMethod">PUT请求</a></div><div><a @click="deleteMethod">DELETE请求</a></div></div>
</template><script>
import axios from 'axios';export default {name: 'HelloWorld',data() {return {url: 'http://192.168.122.130:8080/',message: ''}},methods: {getMethod() {let that = thisaxios.get(this.url).then(response => (that.message = response)).catch(function (error) {that.message = errorconsole.log(error);});},getMethodWithHeader() {let that = thisaxios.get(this.url,{headers: { 'name': 'hello' }}).then(response => (that.message = response)).catch(function (error) {that.message = errorconsole.log(error);});},postMethod() {let that = thisaxios.post(this.url).then(response => (that.message = response)).catch(function (error) {that.message = errorconsole.log(error);});},putMethod() {let that = thisaxios.put(this.url).then(response => (that.message = response)).catch(function (error) {that.message = errorconsole.log(error);});},deleteMethod() {let that = thisaxios.delete(this.url).then(response => (that.message = response)).catch(function (error) {that.message = errorconsole.log(error);});},},}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
a {color: #42b983;cursor: pointer;
}
</style>

后端服务

后端使用python的web框架fastpai

# main.py
from typing import Unionfrom fastapi import FastAPIapp = FastAPI()@app.get("/")
def get_root():return {"请求类型": "GET"}@app.post("/")
def post_root():return {"请求类型": "POST"}@app.put("/")
def put_root():return {"请求类型": "PUT"}@app.delete("/")
def delete_root():return {"请求类型": "DELETE"}@app.get("/hello")
def hello():return {"hello": "world"}

启动:uvicorn main:app --host 0.0.0.0 --port 8089

nginx配置

nginx proxy的配置方法:https://nginx.org/en/docs/http/ngx_http_proxy_module.html

前端配置

/etc/nginx/conf.d/frontend.conf

 server {listen       80 default_server;server_name  localhost;# 配置根目录的地址是以 nginx 下的 html 文件夹为根目录来查找的root /opt/frontend/dist;location / {try_files $uri $uri/ /index.html;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}

后端配置

/etc/nginx/conf.d/backend.conf

server {listen       8080;server_name  backend;location / {proxy_pass       http://192.168.122.130:8089;proxy_set_header Host      $host;proxy_set_header X-Real-IP $remote_addr;}
}

解决跨域问题

配置好nginx之后,重启nginx。并使用浏览器访问前端地址:http://192.168.122.130/,同时按F12打开debug模式。

解决No 'Access-Control-Allow-Origin' header

点击“GET请求”,前端向后端发起请求,会发现请求错误,错误信息如下:

Access to XMLHttpRequest at 'http://192.168.122.130:8080/' from origin 'http://192.168.122.130' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这是因为没有添加跨域请求头。

  • 跨域请求头描述请参见Access-Control-Allow-Origin
  • nginx添加请求头请参考ngx_http_headers_module。

修改nginx配置文件:/etc/nginx/conf.d/backend.conf

server {listen       8080;server_name  backend;location / {proxy_pass       http://192.168.122.130:8089;proxy_set_header Host      $host;proxy_set_header X-Real-IP $remote_addr;# 允许http://192.168.122.130访问后端资源(注意不要添加端口号,因为http://192.168.122.130默认使用的是80端口号,如果是其他端口号就需要添加)add_header Access-Control-Allow-Origin 'http://192.168.122.130' always;}
}

重启nginx,此时前端调用后端接口正常。

解决preflight request

现在让我们尝试其他请求方法。依次点击“带自定义请求头的GET请求”,“POST请求”,“PUT请求”和“DELETE请求”后,可以发现,只有“POST请求“能够正常访问,其他请求都会失败,同时浏览器向后端发起了两个请求。

浏览器报错:

192.168.122.130/:1 Access to XMLHttpRequest at 'http://192.168.122.130:8080/' from origin 'http://192.168.122.130' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

后端服务器的日志:

INFO:     192.168.122.130:35376 - "OPTIONS / HTTP/1.0" 405 Method Not Allowed
INFO:     192.168.122.130:35380 - "POST / HTTP/1.0" 200 OK
INFO:     192.168.122.130:35382 - "OPTIONS / HTTP/1.0" 405 Method Not Allowed
INFO:     192.168.122.130:35384 - "OPTIONS / HTTP/1.0" 405 Method Not Allowed

nginx的日志:

192.168.122.1 - - [20/Jul/2022:15:33:25 +0000] "GET / HTTP/1.1" 200 22 "http://192.168.122.130/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
192.168.122.1 - - [20/Jul/2022:15:33:26 +0000] "OPTIONS / HTTP/1.1" 405 31 "http://192.168.122.130/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
192.168.122.1 - - [20/Jul/2022:15:33:27 +0000] "POST / HTTP/1.1" 200 23 "http://192.168.122.130/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
192.168.122.1 - - [20/Jul/2022:15:33:28 +0000] "OPTIONS / HTTP/1.1" 405 31 "http://192.168.122.130/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
192.168.122.1 - - [20/Jul/2022:15:33:29 +0000] "OPTIONS / HTTP/1.1" 405 31 "http://192.168.122.130/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"

从日志中可以发现,“带自定义请求头的GET请求“,”PUT请求“和”DELETE请求“都向后端发起了OPTIONS请求。

这个OPTIONS其实是一个Preflight request。

A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware using specific methods and headers.

It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header.

这里的“POST请求“和”GET请求“属于simple requests,因此不会有Preflight request,其他请求因为带了复杂的请求头或会修改后端服务的资源,所以需要发起Preflight request判断后端服务是否允许这些操作。

修改nginx配置文件,处理Preflight request

server {listen       8080;server_name  backend;location / {proxy_pass       http://192.168.122.130:8089;proxy_set_header Host      $host;proxy_set_header X-Real-IP $remote_addr;# 允许http://192.168.122.130:80访问后端资源add_header Access-Control-Allow-Origin 'http://192.168.122.130';if ($request_method = 'OPTIONS') {# 允许http://192.168.122.130访问后端资源add_header Access-Control-Allow-Origin 'http://192.168.122.130';# 允许http://192.168.122.130使用GET和PUT访问后端资源add_header Access-Control-Allow-Methods 'PUT';return 204;}}
}

backend.conf配置里添加了对OPTIONS方法的处理,如果前端发起OPTIONS请求,就返回204状态码,同时返回Access-Control-Allow-OriginAccess-Control-Allow-Methods响应头。

add_header Access-Control-Allow-Methods 'PUT';只允许前端使用GETPOSTPUT方式对后端服务发起请求,GETPOST属于simple requests,所以默认允许访问。

现在,可以点击“PUT请求”可以正常访问了,但是“带自定义请求头的GET请求”和“DELETE请求”还不能正常访问,因为响应头Access-Control-Allow-Methods没有添加DELETE方法。

解决 Request header field问题

“带自定义请求头的GET请求”不能正常,是因为“带自定义请求头的GET请求”添加了自定义请求头name

Access to XMLHttpRequest at 'http://192.168.122.130:8080/' from origin 'http://192.168.122.130' has been blocked by CORS policy: Request header field name is not allowed by Access-Control-Allow-Headers in preflight response.

修改nginx配置文件

server {listen       8080;server_name  backend;location / {proxy_pass       http://192.168.122.130:8089;proxy_set_header Host      $host;proxy_set_header X-Real-IP $remote_addr;# 允许http://192.168.122.130:80访问后端资源add_header Access-Control-Allow-Origin 'http://192.168.122.130';if ($request_method = 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://192.168.122.130';# 允许http://192.168.122.130使用GET和PUT访问后端资源add_header Access-Control-Allow-Methods 'PUT';# 允许http://192.168.122.130使用请求头name访问后端资源add_header Access-Control-Allow-Headers 'name';return 204;}}
}

重启nginx,现在“带自定义请求头的GET请求”可以正常访问了。

好啦,现在基本解决了前后端的跨域问题,如果还遇到其他额外问题,都可以在MDN和nginx上找到答案。

参考

跨域问题可以在MDN中找到;nginx配置可以在nginx官网查看。

  • nginx反向代理:https://nginx.org/en/docs/http/ngx_http_proxy_module.html
  • nginx添加请求头:https://nginx.org/en/docs/http/ngx_http_headers_module.html
  • CORS:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
  • Access-Control-Allow-Headers:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
  • Access-Control-Allow-Methods:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
  • Access-Control-Allow-Methods:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
  • vue:https://cn.vuejs.org/v2/guide/index.html
  • fastapi:https://fastapi.tiangolo.com

nginx处理前后端分离跨域问题相关推荐

  1. 前后端分离跨域问题解决方案

    问题 因为最近在学习vue和springboot.用到了前后端分离.前端webpack打包运行的时候会启动nodejs的服务器占用8080端口,后端springboot自带tomcat启动占用1111 ...

  2. pc网站调用微服务器,【微服务】前后端分离-跨域问题和解决方案

    跨域问题存在的原因 跨域问题的根本原因:因为浏览器收到同源策略的限制,当前域名的js只能读取同域下的窗口属性.什么叫做同源策略?就是不同的域名, 不同端口, 不同的协议不允许共享资源的,保障浏览器安全 ...

  3. 【python学习笔记】关于python Flask前后端分离跨域问题

    关于python Flask前后端分离跨域问题 前后端分离过程中,前后端对接测试难免遇到跨域问题.因为是个新司机,所以在我经过一天的测试,才找到解决办法=-= 第一种方法 from functools ...

  4. Springboot整合Shiro前后端分离跨域问题

    Springboot整合Shiro前后端分离跨域问题 前言:SpringBoot整合shiro进行前后端分离开发时(前端是Vue),项目做了跨域配置,但还是前端请求会出现cros err–显示的跨域问 ...

  5. cors 前后端分离跨域问题_前后端分离之CORS跨域访问踩坑总结

    前言 前后端分离的开发模式越来越流行,目前绝大多数的公司与项目都采取这种方式来开发,它的好处是前端可以只专注于页面实现,而后端则主要负责接口开发,前后端分工明确,彼此职责分离,不再高度耦合,但是由于这 ...

  6. 前后端分离跨域问题Access to XMLHttpRequest at ‘http://localhos...has been blocked by CORS policy: No ‘Access-

    完整报错如下: Access to XMLHttpRequest at 'http://localhost:8081/login' from origin 'http://localhost:8084 ...

  7. nginx配置反向代理解决前后端分离跨域问题

    2019独角兽企业重金招聘Python工程师标准>>> 摘自<AngularJS深度剖析与最佳实践>P132 nginx配置文件如下: server {listen 80 ...

  8. Nginx的简单使用,配置多前端,多端口【微信小程序+前后端分离跨域解决】

    微信小程序 微信小程序需要服务器要有域名,不能有端口,但是我还有一个WebSocket的wss协议路径需要填,都是后台的 示例配置文件,配置https转发http,配置https转发wss user ...

  9. 前后端分离 跨域问题解决

    跨域的实现原理 http 跨域请求后端服务的时候会在请求头中带上如下信息 Origin: http://api.jijs.com 后端服务器需要在http的响应头中添加以下响应头 Access-Con ...

最新文章

  1. gui - tkinter 开发
  2. git stage 暂存_什么是Git?下载和安装Git
  3. hdu4280 Island Transport 网络流最大流 Dinic算法高效模板
  4. 光纤收发器在高清网络视频监控工程项目中的应用
  5. hdu 3074 线段树 OR 树状数组
  6. 性能监控工具javamelody与spring的集成
  7. 笔记:Microservices for Java Developers
  8. CSS / CSS3(新增)选择器及优先级原则
  9. ipad分屏大小怎么调整_flash怎么调整元素大小-Adobe flash统一图形大小的方法
  10. python画图保存网页_一起学Python数据分析——引言
  11. xxx is not in the sudoers file. This incident will be reported.
  12. Learning Scrapy笔记(零) - 前言
  13. Android Studio的适配器Adapter以及Adapterview的使用
  14. Springboot bean作用域
  15. pip install 安装的问题
  16. 爬取斗鱼房间号信息-计算在线观看总人数
  17. Unity:SLG游戏画线模块
  18. 【唯一不变的是—那个夏天,你纯真的眼神】
  19. 烟雨白银坨_SAP刘梦_新浪博客
  20. Android elevation使用

热门文章

  1. Java 类加载机制(二)
  2. 如何利用地表温度遥感数据和气象资料计算农田地表水热通量
  3. Leetcode 788: Rotated Digits
  4. 企业级负载均衡解决方案之三:唯品会四层负载均衡解决方案VGW
  5. mac和linux都能识别的u盘格式化,在终端使用Mac diskutil 命令格式化顽固U盘
  6. 如何修改应用程序的图标以及exe文件的图标
  7. 配电运维日常电力现状及畅谈配电行业的多样化
  8. java 8583报文解析_8583报文解析实例
  9. 网友自制石膏面具可解锁iPhone X
  10. PyTorch实现基于ResNet18迁移学习的宝可梦数据集分类