强制 Content-Type 或在 Flask 中公开 request.data 以获得已知的内容类型

Posted

技术标签:

【中文标题】强制 Content-Type 或在 Flask 中公开 request.data 以获得已知的内容类型【英文标题】:Force Content-Type or expose request.data in Flask for known content-type 【发布时间】:2011-10-28 12:34:53 【问题描述】:

我正在 Python/Flask 中重新创建服务,并且遇到了现有客户端身份验证方式的问题。出于兼容性原因,我必须匹配现有的客户端方案。

现有客户端采用用户名、密码并对其进行 base64 编码。这不是 HTTP 基本身份验证,尽管听起来很相似。下面是一些创建此登录请求的示例代码。

credentials = 
            'username': 'test@example.com',
            'password': 'password'

data = b64encode(urlencode(credentials))
request = urllib2.Request(loginURL)
request.add_data(data)
# request.add_header('Content-Type', 'application/gooblygop')
# 'application/x-www-form-urlencoded' seems to be a default Content-Type
login = urllib2.urlopen(request)

在服务器端,我获取 POST 数据并对其进行 base64 解码以再次获取用户名和密码信息。

flask server:
@app.route('/login', methods=['POST'])
def login():
    error = None
    if request.method == 'POST':
        # post data: cGFzc3dvcmQ9ZGVmYXVsdCZlbWFpbD10ZXN0JTQwZXhhbXBsZS5jb20=
        data = b64decode(request.data)
        # decoded data: password=default&email=test%40example.com
        return('ok')

问题在于内容类型。如果我在客户端(应用程序/gooblygop)中指定了未知的 Content-Type,Flask 会将 POST 数据公开给 request.data,我可以解码 base64 字符串。如果我将 Content-Type 保留为默认值(application/x-www-form-urlencoded),则原始数据不会暴露给 request.data 并且我不知道如何检索 base64 编码的字符串并使用它。

现有的客户端软件几乎都默认为 x-www-form-urlencoded,但我不能总是这样。

基本上,我需要一种可靠的服务器端方法来访问该编码字符串,无论客户端程序声明什么 Content-Type。

其他说明:我对 Python 非常陌生,来自 php 背景。所以我非常愿意接受建议。另外,本项目主要供个人使用。

【问题讨论】:

【参考方案1】:

在处理具有正常 mimetype 的 urlencoded 帖子时,您希望查看 request.form 对象。在这种情况下,您有一个不寻常的形式,但这里有一种方法:

# mkreq.py
from urllib import urlencode
import urllib2
from base64 import b64encode

credentials = 
            'username': 'test@example.com',
            'password': 'password'

data = b64encode(urlencode(credentials))
request = urllib2.Request("http://localhost:5000/login")
request.add_data(data)
request.add_header('Content-Type', 'application/gooblygop')
# 'application/x-www-form-urlencoded' seems to be a default Content-Type
login1 = urllib2.urlopen(request).read()
print(login1)
request2 = urllib2.Request("http://localhost:5000/login")
request2.add_data(data)
login2 = urllib2.urlopen(request2).read()
print(login2)

您可能想要修改登录位以检查 mimetype,这里是一个对您当前设置的更改最小的版本:

@app.route('/login', methods=['POST'])
def login():
    error = None
    if request.method == 'POST':
        # post data: cGFzc3dvcmQ9ZGVmYXVsdCZlbWFpbD10ZXN0JTQwZXhhbXBsZS5jb20=
        data = b64decode(request.data)
        # decoded data: password=default&email=test%40example.com
        if not data:
            data = b64decode(request.form.keys()[0])
        special_mimetype = request.mimetype
        return(special_mimetype + '\n' + data)

这是第一个代码示例的输出,带有两个请求:

bvm$ python mkreq.py
application/gooblygop
username=test%40example.com&password=password
application/x-www-form-urlencoded
username=test%40example.com&password=password

【讨论】:

【参考方案2】:

您是否考虑过使用 json 在 POST 中传递数据? Flask 内置了对传递 json 数据的支持。另外,如果你在 headers 中设置 Content-Type 为 application/json ,flask 会自动为你解压 POST 数据并放入 request.json

这是请求的应用程序

import urllib2
import json

if __name__ == "__main__":
  headers = 'Content-Type':'application/json'
  post_data = "user":"test_user"
  print "Posting request"
  req = urllib2.Request("http://localhost:5000/login", json.dumps(post_data), headers)
  resp = urllib2.urlopen(req)
  print "Response was %s" % resp.read()  

这是 Flask 视图

from flask import request

@app.route('/login', methods=['POST'])
 def login():

  user = request.json['user']
  return user

如果您使用的是 linux 终端,我建议您也使用 curl 进行测试。这是一个例子

curl -X POST -H "Content-Type:application/json" -s -d '"user":"This is the username"' 'localhost:5000/login'

This is the username

【讨论】:

以上是关于强制 Content-Type 或在 Flask 中公开 request.data 以获得已知的内容类型的主要内容,如果未能解决你的问题,请参考以下文章

如何强制 Flask/SQLAlchemy 创建表?

随笔 | Python的Flask强制使http跳转到https页面

卷曲设置 Content-Type 不正确

Angular 2 http post 作为空对象或在关键端发送

前端与后端的数据交互(jquery ajax+python flask)

OSB 电子邮件 - 强制多部分/混合