在Flask中强制设置Content-Type或暴露已知Content-Type的request.data

7 投票
2 回答
5662 浏览
提问于 2025-04-16 23:44

我正在用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')

问题出在内容类型上。如果我在客户端指定一个未知的内容类型(比如application/gooblygop),Flask会把POST数据暴露给request.data,这样我就可以解码base64字符串。如果我把内容类型保持为默认(application/x-www-form-urlencoded),原始数据就不会暴露给request.data,我就不知道怎么获取这个base64编码的字符串并使用它。

现有的客户端软件基本上都是默认使用x-www-form-urlencoded,但我不能总是依赖这一点。

总的来说,我需要一个可靠的服务器端方法来访问这个编码字符串,无论客户端程序声明的内容类型是什么。

其他说明:我对Python还很陌生,之前是用PHP,所以我很欢迎任何建议。此外,这个项目主要是为了个人使用。

2 个回答

1

你有没有想过用json格式来传递你的数据呢?Flask框架本身就支持传递json数据。而且,如果你在请求的头部设置Content-Type为application/json,Flask会自动把POST的数据转换成json格式,并放到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
2

当你处理使用普通的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

撰写回答