我们的Django应用程序处理(Unix)账户请求的大部分工作只有教授等特殊人员可以做,他们可以实际批准账户请求,而不仅仅是提出请求。按照惯例,我们用Apache HTTP基本认证来保护网络应用程序的管理部分,只有指定的Unix组(如 "赞助商 "组)的人可以访问。 然而,Django应用程序也有一个 "用户 "表,为了访问该应用程序,你必须在其中(以及在一个允许访问的Unix组中)。通常情况下,我们保持这两件事的同步;当我们把某人添加到相关的Unix组时,我们也把他们添加为用户(并设置他们的用户类型,以及为可以赞助账户的人设置其他必要的数据)。但有时我们忽略了这个额外的步骤,所以人们被Apache允许,但不在 "用户 "表中。 如果他们真的试图使用我们的网络应用程序,这将导致它以错误方式停止。
(这个 "用户 "表是应用程序自己的一组表和模型的一部分,而不是正常的Django认证系统的 "用户 "表。 也许我应该以不同的方式处理这个问题,使其与正常的Django的东西更加集成,但当我开始这个Web应用程序时,我是Django的新手,将事情完全分开是更容易的。)
现在,我们的一堆视图在开始时是这样的:
def approve(request):
urec = get_user(request)
if urec.is_staff():
...
...
你会注意到,这里没有错误处理。这是因为get_user() 做的是蛮力的简单事情(去掉了一些错误检查):
def get_user(request):
user = request.META['REMOTE_USER']
try:
return models.User.objects.get(login=user)
except models.User.DoesNotExist:
... log a message ...
raise django.http.Http404
这很简单,也很可靠,但它有一个缺点,那就是遇到我们这个错误的人得到的HTTP 404错误页面和他们在试图进入一个在我们的应用程序中真正不存在的URL时得到的一样。这在最好的情况下是没有信息的,在最坏的情况下是令人困惑的,我希望能做得更好。不幸的是,我不确定最好的方法是什么。
我的第一次尝试是用一个特定的信息字符串引发Django的Http404错误,然后尝试让我们的应用程序的404错误页面模板检查该字符串,并生成一组不同的信息。这个方法失败了,因为据我所知,要么Django在处理过程中的某个点上丢掉了消息字符串,要么没有把它作为模板变量传给你的自定义模板。
我看到有三种替代方法,但我都没有被说服。简单但不讨好的办法是改变get_user(),在这种情况下返回一个错误。这将需要在它被调用的每个地方做一个模板修改,以检查错误并通过生成一个标准的 "我们搞砸了 "的响应页面来处理它,这使代码感觉像Go而不是Python。但至少事情的运作方式是显而易见的(如果我返回None ,我可以让失败处理这种情况相对明显)。
更复杂但代码更少的方法是引发一个自定义错误,并用一个捕捉错误的装饰器来包装每个调用get_user() 的函数,以生成并返回标准的解释页面。我将不得不装饰每一个直接或间接调用get_user() 的视图函数(如果我添加了新的函数,还要记得添加这个),而装饰器是一种高级的Python魔法,对于人们来说,不一定清楚或直接地遵循。
我怀疑在Django中我应该用某种形式的中间件来做这件事。如果我保持目前的方法,我可以用一个中间件来做这件事,它只需使用process_exception(),但这似乎不是最习惯的方式。由于这是任何受HTTP基本认证保护的事物的常见处理步骤,感觉中间件应该自己做用户查询,并以某种方式将其附加到请求中,而实际的视图甚至不调用get_user() 。但我不知道如何将任意数据附加到Django的请求对象上,而且无论如何,这涉及到比捕捉自定义异常的中间件更多的Django魔法(而且这种魔法不那么清晰,因为视图函数将只是访问数据而没有任何明显的理由)。
(我关心任何解决方案中涉及的魔法数量,因为我的同事对Django不是特别熟悉,甚至我也只是偶尔碰一下代码。可能这意味着我应该直接使用明确的错误检查版本,即使它让我抽搐)。