django修改请求对象


72

我已经有一个django项目,它的逻辑性如下:

网址:URL?username = name&pwd = passwd

视图:

def func(request):
   dic = request.GET

   username = dic.get("username")
   pwd = dic.get("pwd")

但是现在我们需要加密数据。然后,请求变为:

网址:URL?crypt = XXXXXXXXXX(XXXXXXXX是为“ username = name&pwd = passwd”加密的字符串)

所以我需要修改每个视图函数。但是现在我想在django中间件中解密,以防止修改每个视图函数。

但是当我修改request.GET时,我收到错误消息“此QueryDict实例是不可变的”。我该如何修改?


13
在网址中发送用户名和密码是一个非常糟糕的主意。
斯里尼瓦斯·雷迪·塔蒂帕西

1
为什么您需要在网址本身中发送用户名和密码?
Sudip Kafle 2013年

Answers:


116

django.http.QueryDict分配给request.GET并且request.POST不可变的对象。

您可以QueryDict通过复制将其转换为可变实例:

request.GET = request.GET.copy()

之后,您将能够修改QueryDict

>>> from django.test.client import RequestFactory
>>> request = RequestFactory().get('/')
>>> request.GET
<QueryDict: {}>
>>> request.GET['foo'] = 'bar'
AttributeError: This QueryDict instance is immutable
>>> request.GET = request.GET.copy()
<QueryDict: {}>
>>> request.GET['foo'] = 'bar'
>>> request.GET
<QueryDict: {'foo': 'bar'}>

这是经过专门设计的,因此不允许任何应用程序组件编辑源请求数据,因此,即使QueryDict再次创建不可变的对象,也会破坏该设计。我仍然建议您遵循准则,并直接在request中间件中的对象上分配其他请求数据,尽管这可能会导致您编辑源代码。


13
在Django 2.0中,当我尝试执行此操作时,出现AttributeError“无法设置属性”
cvipul

54

删除不变性:

if not request.GET._mutable:
   request.GET._mutable = True

# now you can spoil it
request.GET['pwd'] = 'iloveyou'

更新资料

Django认可的方法是:request.GET.copy()

根据文档

在正常的请求/响应周期中访问request.POST和request.GET时的QueryDicts将是不可变的。要获得可变版本,您需要使用QueryDict.copy()。

没有任何保证可以保证将来的Django版本将使用_mutable。这比copy()方法有更多的更改机会。


4
这样更好地避免了与POST请求上的copy()相关的成本
Kevin Parker

15
...但是更糟,因为它修改了内部属性。
David Neale

6
设置request.GET._mutable = False完值后,您可以执行以下操作:使它再次变为不变。
Caumons

8
请不要建议这样做。
Josh Smeaton

1
谁能告诉我“这种方法安全吗?”
Rai Ammad Khan

8

您不应该使用GET发送用户名和密码,这是一种不好的做法(因为它在URL栏上显示了信息,并且可能会带来安全风险)。而是使用POST。另外,我猜您正在尝试对用户进行身份验证,似乎您在做太多工作(创建新的中间件)来处理完全内置的内容,以docs为例:

from django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request, user)
            # Redirect to a success page.
        else:
            # Return a 'disabled account' error message
    else:
        # Return an 'invalid login' error message.

我自己真的很喜欢使用login_required装饰器,使用起来非常简单。希望能有所帮助


感谢您的回答。这只是一个例子。我们不仅使用GET,而且使用POST。即使使用post,我们也必须加密数据,因为恐怕有些工具(例如tcpdump)抓取数据包,因此加密/解密是必要的。使我困惑的问题是如何尽可能少地修改视图功能。所以我想使用中间件
user2801567

查看有关创建自己的中间件的文档。您想使用process_request或process_view,但请记住访问那些请求。其中的POST将导致视图无法运行(csrf是该规则的一个例外,因此请研究它如何工作以弄清楚如何编写自己的规则)docs.djangoproject.com/en/dev/topics/http/middleware
yuvi 2013年

1
GET请求本身没有安全性问题。这是一种不好的做法,因为它们位于地址栏中……但是我只想确保每个人都清楚,在HTTPS上,GET和POST有效负载在传输过程中受到同等保护。通过HTTP,它们同样不受保护。
亚当·尼尔森

1
@AdamNelson如果您将任何内容传递到GET请求中,则它将在日志文件,Internet历史记录和重定向HTTP标头中可用。即使通过HTTPS也不是完全安全的。它们在运输过程中没有得到同样的保护。
约旦

1
@Jordan日志文件和互联网历史部分是合法的侧通道攻击,你是对的,他们做比POST安全变得更糟,但里面的HTTPS信封的保护是完全一样的无论是POST或GET
亚当纳尔逊

1
request.GET._mutable = True

你需要这个。

def func(request):
   dic = request.GET
   request.GET._mutable = True #to make it editable 
   username = dic.get("username")
   request.GET.pop("pwd")
   request.GET._mutable = False #make it False once edit done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.