Flask 和 jQuery 联合实现传送 JSON 数据的 POST 跨域请求 (CORS)

介绍

跨域资源共享 CORS: Cross Origin Resource Sharing,通常的应用情景是,你的网页,包括 HTML/CSS/JS, 被 serve 在一个服务器的特定端口上,比如 80,但是现在网页上需要请求其他服务器上的资源,无论是静态还是动态的。请求的方式大多数时候是 GET,有的时候需要 POST。但是因为安全问题,浏览器会对跨域请求进行限制。

本文会演示一个简单的跨域请求。从客户端 (浏览器) 发送请求,到收到响应的过程如下:

  1. 用户输入数据,并点击按钮触发代码运行
  2. 客户端运行 JavaScript 获得用户输入的数据,整理成 JSON 格式
  3. 客户端将 JSON 数据发送到另一个服务器地址 (跨域)
  4. 另一个服务器收到 JSON 数据,并对数据进行处理
  5. 服务器将处理过的数据格式化为 JSON 格式,并将结果返回客户端
  6. 客户端收到返回的结果,若整个过程成功,则将返回的数据填入页面的某处,若失败,则在浏览器控制台输出错误

服务器用的是最基本的 Flask,客户端用的是 jQuery。



服务器接受 POST 请求,读取数据,处理后返回响应结果

这个小的 Flask 服务器程序,与普通的 Flask server 唯一的不同是导入了 CORS 模块,这个需要你先安装:

pip3 install flask_cors

这之后,你可以在你的服务器上进行一个简单的设定,决定哪些 URL 可以接受来自客户端的跨域请求。

Python 代码:

from flask import Flask, request, jsonify
from flask_cors import CORS

# 设定一个应用程序
app = Flask(__name__)

# r'/*' 是通配符,让本服务器所有的 URL 都允许跨域请求
CORS(app, resources=r'/*')

# 根目录返回一个问候字符串
@app.route("/")
def hello():
    string = "Hello, I am a Flask server running inside a Docker container on a VM instance of Google Cloud Platform!\nNice to meet you!"
    return string

# /json URL 会接受 POST 请求
@app.route("/json", methods=['GET','POST'])
def color():
    
    # 服务器上的日志记录
    app.logger.debug("JSON received...This is interesting!")
    app.logger.debug(request.get_json(force=True))
    
    # 取得客户端 POST 过来的 json 数据
    request_json = request.get_json(force=True)
    
    # 当正常接收到数据时,执行以下步骤
    if request_json: 
        # 从字典中取出数据
        name = request_json['name']
        gender = request_json['gender']
         
        # 处理数据
        name = name + 'ROCK'
        gender = gender + 'ROLL'

        # 服务器端打印结果,仅供肉眼检查
        print(name)
        print(gender)

        # 返回 JSON 格式的数据
        return jsonify({
            "name": name,
            "gender": gender
            })
    else:
        # 若发生错误,未接收到数据,则返回错误信息
        return "Sorry, no json data received."

if __name__ == "__main__":
    app.run()

把这些代码保存为文件 app.py,然后在当前目录运行:
python3 app.py

这样你的另一个服务器就开启了,它有两个 URL,都可以接受跨域请求:



客户端获得用户输入并发送 POST 请求给另一个服务器

一般来讲,AJAX 用于从浏览器中直接向网页所在的服务器请求新的资源,避免让用户频繁刷新页面,造成时间上的延迟。此处的 AJAX 请求与普通的 jQuery AJAX 请求不同的是,加了一个 'crossDomain: true',表示允许浏览器发起跨域请求,可以去请求其他服务器 IP 地址上的资源。

页面也许类似于这样的:

JavaScript 代码:

 $(document).ready(function() {
  // 发送按钮被点击时干这些事儿
  $('#submit').click(function() {
    // 从页面元素中获得数据
    request_name = $('#request-name').val();
    request_gender = $('#request-gender').val();

    // 保存为字典格式
    var send_info = {
      'name': request_name,
      'gender': request_gender
    };

    // 发起 AJAX 请求
    $.ajax({
      type: 'POST',    // 请求方式
      url: 'http://xxx.xxx.xxx.xxx:80/json',    // 另一个服务器 URL
      crossDomain: true,    // 允许跨域请求
      data: JSON.stringify(send_info),    // 将数据 JSON 化
      dataType: 'json',

      // 发送数据并成功收到响应时干嘛?
      success: function(responseData, textStatus, jqXHR) {
        console.log(textStatus);
          
        // 用 '.' 加字段名直接从 responseData 中取得数据
        var response_name = responseData.name;
        var response_gender = responseData.gender;
        
        // 控制台打印结果,供肉眼检查
        console.log(response_name);
        console.log(response_gender);

        // 将获得的响应结果添加到页面相应的元素中
        $('#response-name').val(response_name);
        $('#response-gender').val(response_gender);

        console.log("Success!");
      },

      // 响应失败时干嘛?
      error: function (responseData, textStatus, errorThrown) {
        console.log(responseData);
        console.log(textStatus);
        console.log(errorThrown);
        // 控制台查看响应文本,排查错误
        console.log(responseData.responseText);
        console.log('Oh, POST failed for some reason.');
      }
    });
  });
});

注意这其中的数据格式,data: JSON.stringify(send_info),这个地方会有一些坑,不同的版本会有不同的结果,需要尝试一下什么样的数据可以顺利发送与接收。

Happy coding!


相关链接:

跨域资源共享 CORS 详解 - 阮一峰的网络日志HTTP访问控制(CORS)
HTTP access control (CORS)

Flask-CORS - Flask-Cors 3.0.2 documentation
Solve Cross Origin Resource Sharing with Flask

How do I send a cross-domain POST request via JavaScript?
API - Flask Documentation (0.12)
编辑于 2017-06-22

文章被以下专栏收录