解决系统开发中的跨域问题:CORS、JSONP、Nginx

文章目录

  • 一、概述
    • 1.问题场景
    • 2.浏览器的同源策略
    • 3.解决思路
  • 二、一点准备工作
    • 1.创建前端工程1
    • 2.创建后端工程
    • 3.创建前端工程2
    • 4.跨域问题
  • 三、方法1:使用CORS
  • 四、方法2:JSONP
  • 五、方法3:Nginx
    • 1.安装和启动(windows)
    • 2.使用Nginx配置转发规则
    • 3.修改后端工程的ip
    • 4.前端代码修改

本博客配套的源码在这里

一、概述

1.问题场景

最近我在做一个系统的全栈开发,遇到了这样一个问题。

首先,我的前端是一个来自百度的开源框架——Amis,它封装自React.js,基于JSON配置。我下载了Amis提供的SDK文件夹,并进行了代码开发。但是我在部署整个系统的时候遇到了跨域问题。原因是,我的前端不是以服务的形式运行的,它是一组在浏览器中打开的HTML页面。

如果我在浏览器中打开一个HTML页面,当前采用的协议通常是HTTP或HTTPS,域名通常是"localhost"或者是HTML文件所在的服务器的域名,端口通常是80(HTTP)或443(HTTPS)。由于我是通过文件路径直接打开HTML文件,那么协议、域名和端口都是本地文件系统的相关信息。

而我需要在前端中调用后端的接口,尽管后端IP与前端一致,但PORT和前端不同,因此在浏览器中访问系统时,触发了浏览器的同源策略,导致我的前端无法访问后端接口。

2.浏览器的同源策略

解决系统开发中的跨域问题:CORS、JSONP、Nginx_第1张图片

根据同源策略,浏览器会阻止页面中的JavaScript代码向不同域名、协议或端口的资源发出跨域请求。这意味着如果我的HTML页面和后端服务的域名、协议或端口不一致,浏览器会阻止这种跨域请求。

3.解决思路

我上网查阅了资料,发现,在遵守同源策略的前提下,可以采取以下方法来实现前端页面对后端服务的访问:

  1. 使用CORS(跨域资源共享):在后端服务中配置允许特定域名的跨域请求,通过设置响应头来允许跨域访问。这样可以让前端页面在浏览器中向后端服务发出跨域请求。
  2. JSONP(JSON with Padding):在一些旧版浏览器中,可以通过JSONP来进行跨域请求。不过需要注意JSONP存在一些安全性方面的问题,需要谨慎使用。
  3. 代理服务器:在开发环境中,可以设置代理服务器来转发前端请求到后端服务,使得前端页面和后端服务在同一个域名下,从而避免跨域问题。

下面我将对这三种方法进行实践。

二、一点准备工作

1.创建前端工程1

前端工程1我写了一个HTML页面,放在HTML目录下。在点击按钮后,显示弹窗,如果后端顺利返回,则将返回的文本显示到弹窗上;如果发生异常,在弹窗上显示异常信息。

home.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>调用后端服务title>
head>
<body>
    <h1>调用后端服务示例h1>
    <button id="getDataBtn">获取数据button>

    <script>
        document.getElementById('getDataBtn').addEventListener('click', function() {
            fetch('http://127.0.0.1:2020/api/hello')
                .then(response => {
                    if (!response.ok) {
                        throw new Error('Network response was not ok');
                    }
                    return response.text();
                })
                .then(data => {
                    alert(data);
                })
                .catch(error => {
                    alert('发生错误: ' + error.message);
                });
        });
    script>
body>
html>

2.创建后端工程

我创建了一个Flask项目,因为Flask足够的简单、快捷,但是你也可以使用任何你熟悉的语言和框架。

使用之前需要本地有python环境,并执行pip install flask来安装依赖,并在我提供的源码的FLASK目录下执行python app.py来运行项目。

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/api/hello', methods=['GET'])
def get_data():
    return jsonify(data='hello,flask!')

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=2020,debug=True)

3.创建前端工程2

我想找到不同形式的前端对应的跨域问题的解决方案,因此我创建了两种前端,除了上面的HTML页面的形式,还包括了服务的形式。

我创建了一个vue.js脚手架项目,并在里面写了和前端工程1类似的代码。关于怎么快速上手vue,可以看我的另一篇博客:这里

在VUE/vue-app目录下执行npm run serve运行项目。

App.vue

<template>
  <div>
    <button @click="getData">获取数据</button>
    <div v-if="showModal" class="modal">
      <div class="modal-content">
        <span v-if="responseData">{{ responseData }}</span>
        <span v-if="error">{{ error }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showModal: false,
      responseData: null,
      error: null
    };
  },
  methods: {
    getData() {
      fetch('http://127.0.0.1:2020/api/hello')
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.text();
        })
        .then(data => {
          this.responseData = data;
          this.showModal = true;
        })
        .catch(error => {
          this.error = '发生错误: ' + error.message;
          this.showModal = true;
        });
    }
  }
};
</script>

<style>
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
}

.modal-content {
  background-color: white;
  padding: 20px;
  border-radius: 5px;
}
</style>

4.跨域问题

  • 前端工程1——HTML:

在浏览器中打开home.html页面,点击按钮后,无法成功获取后端数据,触发了浏览器的同源策略。

解决系统开发中的跨域问题:CORS、JSONP、Nginx_第2张图片

  • 前端工程2——VUE:

解决系统开发中的跨域问题:CORS、JSONP、Nginx_第3张图片

下面就来着手解决这个问题。

三、方法1:使用CORS

在后端服务中配置允许特定域名的跨域请求,通过设置响应头来允许跨域访问。这样可以让前端页面在浏览器中向后端服务发出跨域请求。

在 Flask 项目中应用 CORS(app) 的底层原理涉及到在 HTTP响应中添加特定的头部信息,以允许跨域请求访问资源。Flask-CORS 扩展简化了这个过程,它通过在响应中添加适当的 CORS头部信息来实现跨域资源共享。 具体来说,当你在 Flask 项目中调用 CORS(app) 时,Flask-CORS会自动为你的应用程序添加 CORS 头部信息,包括Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 等。这些头部信息告诉浏览器哪些跨域请求是被允许的,从而解决了跨域请求被浏览器阻止的问题。
Flask-CORS 简化了这个过程。

这种方法只能对前端工程是服务(前端工程2)的情况生效,原因就是后端需要配置前端的ip、端口等信息,而前端工程1是以文件的形式打开前端页面并访问后端的。

当以文件路径的形式在浏览器中打开HTML页面文件时,页面中调用后端的API时,无法直接获取调用者的IP和端口。这是因为以文件路径形式打开HTML页面时,页面的请求是直接从文件系统发出的,而不是通过网络协议进行通信,因此无法获取调用者的IP和端口信息。

如果需要获取调用者的IP和端口信息,需要将HTML页面部署到一个服务器上,然后通过服务器地址访问页面,这样页面中的请求就会通过网络协议进行通信,从而可以获取调用者的IP和端口信息。

  • 后端配置CORS:
from flask import Flask,request
from flask_cors import CORS


app = Flask(__name__)

# 配置前端vue的ip、端口
CORS(app, resources={r"/api/hello": {"origins": "http://127.0.0.1:8080"}})


@app.route('/api/hello', methods=['GET'])
def get_data():
    return jsonify(data='hello,flask!')

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=2020,debug=True)

  • 在浏览器中访问vue页面:

解决系统开发中的跨域问题:CORS、JSONP、Nginx_第4张图片

成功访问到了后端!

四、方法2:JSONP

JSONP(JSON with Padding)是一种利用

你可能感兴趣的:(实战,运维,跨域)