return JSON 认证失败时如何输出?

How can I return JSON output when repoze.who authentication fails?

我正在编写 repoze.who plugin 并希望从 repoze.who 身份验证中间件 return JSON 并且仍然控制 HTTP 状态代码。如何做到这一点?

实现此目的的一种方法是实施 repoze.who Challenger interface. The following solution takes advantage of the fact that the WebOb exceptions, in webob.exc, can be used as a WSGI application. The following example shows how this can be used in a hypothetical Facebook plugin, where the 2.x API 让用户 授予对其电子邮件的访问权限,这可能是成功 registration/authentication:

import json
from webob.acceptparse import MIMEAccept
from webob.exc import HTTPUnauthorized, HTTPBadRequest


FACEBOOK_CONNECT_REPOZE_WHO_NOT_GRANTED = 'repoze.who.facebook_connect.not_granted'


class ExampleJSONChallengerPlugin(object):
    json_content_type = 'application/json'
    mime_candidates = ['text/html',
                       'application/xhtml+xml',
                       json_content_type,
                       'application/xml',
                       'text/xml']

    def is_json_request_env(self, environ):
        """Checks whether the current request is a json request as deemed by
        TurboGears (i.e. response_type is already set) or if the http
        accept header favours 'application/json' over html.
        """
        if environ['PATH_INFO'].endswith('.json'):
            return True

        if 'HTTP_ACCEPT' not in environ:
            return False

        # Try to mimic what Decoration.lookup_template_engine() does.
        return MIMEAccept(environ['HTTP_ACCEPT']) \
            .best_match(self.mime_candidates) is self.json_content_type

    def challenge(self, environ, status, app_headers, forget_headers):
        if FACEBOOK_CONNECT_REPOZE_WHO_NOT_GRANTED in environ:
            response = HTTPBadRequest(detail={
                'not_granted': 
                environ.pop(FACEBOOK_CONNECT_REPOZE_WHO_NOT_GRANTED),
            })
        elif status.startswith('401 '):
            response = HTTPUnauthorized()
        else:
            response = None

        if response is not None and self.is_json_request_env(environ):
            response.body = json.dumps({
                'code': response.code,
                'status': response.title,
                'explanation': response.explanation,
                'detail': response.detail,
            })
            response.content_type = self.json_content_type

        return response

这里的中心点是 responsewebob.exc.WSGIHTTPException 的 sub-class 的一个实例,被用作 WSGI 应用程序,而且如果 response' s body 属性被设置,那么它是 not 自动生成的,事实上我们用来显式设置响应的 body 为 JSON-formatted 字符串表示我们的字典。如果在处理对以 '.json' 结尾的 URL 或 Accept header 包含 application/json 的请求期间调用上述挑战者,则 body 的响应可能呈现为:

{
    "status": "Bad Request",
    "explanation": "The server could not comply with the request since it is either malformed or otherwise incorrect.",
    "code": 400, 
    "detail": {"not_granted": ["email"]}
}

如果不是,则 body 将呈现为 HTML:

<html>
 <head>
  <title>400 Bad Request</title>
 </head>
 <body>
  <h1>400 Bad Request</h1>
  The server could not comply with the request since it is either 
  malformed or otherwise incorrect.<br /><br />
{'not_granted': [u'email']}


 </body>
</html>