django:将BadRequest提升为例外?

时间:2014-08-21 08:59:39

标签: python django exception-handling httpresponse

是否可以在django中将BadRequest作为例外提出?

我已经看到你可以筹集404 [1]。

用例:在helper方法中,我从request.GET加载一个json。如果json因为浏览器(IE)切断网址而被切断,我想提出一个匹配的例外。

BadRequest异常看起来合适,但到目前为止django中似乎没有这样的异常。

在1.6中有一个SuspiciousOperation异常。但这与我的情况不符,因为它与安全无关。

当然我可以在view方法中放一个try ..except,但这不是DRY。

有人提供解决方案我不需要在每次调用我的帮助方法时尝试一次吗?

[1] https://docs.djangoproject.com/en/1.6/ref/exceptions/#django.core.urlresolvers.Resolver404

更新

代码示例:

def my_view(request):
    data=load_data_from_request(request) # I don't want a try..except here: DRY
    process_data(data)
    return django.http.HttpResponse('Thank you')

def load_data_from_request(request):
    try:
        data_raw=json.loads(...)
    except ValueError, exc:
        raise BadRequest(exc)
    ...
    return data

6 个答案:

答案 0 :(得分:19)

其他答案解释了如何返回状态为400的HTTP响应。

如果要挂钩Django的400 error handling,可以引发SuspiciousOperation异常或其子类。

请参阅文档herehere

在您的示例中,它看起来像:

from django.core.exceptions import SuspiciousOperation

def load_data_from_request(request):
    try:
        data_raw = json.loads(...)
    except ValueError:
        raise SuspiciousOperation('Invalid JSON')
    # ...
    return data

答案 1 :(得分:13)

您需要自定义中间件来处理异常情况。 您需要具有自定义异常才能在中间件中进行检查。

class ErrorHandlingMiddleware(object):
    def process_exception(self, request, exception):
        if not isinstance(exception, errors.ApiException): # here you check if it yours exception
            logger.error('Internal Server Error: %s', request.path,
                exc_info=traceback.format_exc(),
                extra={
                    'request': request
                }
            )
        # if it yours exception, return response with error description
        try:
            return formatters.render_formatted_error(request, exception) # here you return response you need
        except Exception, e:
            return HttpResponseServerError("Error During Error Processing")

答案 2 :(得分:8)

作为@ coldmind的答案(在中间件层中转换异常)的替代方法,您可以在视图函数上放置一个装饰器来执行相同的操作。我个人更喜欢这个,因为它只是普通的Python,并不需要我了解Django中间件的工作原理。

您不希望在视图函数中内联所有功能(这会使您的视图模块依赖于您项目的所有其他模块,导致'一切都取决于其他所有'的体系结构)相反,它更好如果视图只知道http。它从请求中提取您需要的内容,委托给其他一些“业务逻辑”功能。业务逻辑可以委托给其他模块(例如数据库代码或与其他外部系统的接口。)然后,最终通过视图函数将业务逻​​辑的返回值转换为http响应。

但是如何将错误从业务逻辑(或其委托的任何内容)传回视图函数?由于许多原因,使用返回值是令人厌烦的。例如,这些错误返回值必须从整个代码库中传播回视图。这通常是非常混乱的,因为您已经将函数的返回值用于其他目的。

处理这种情况的自然方法是使用异常,但Django视图本身不会将未捕获的异常转换为返回的HTTP状态代码(除了一些特殊情况,如OP所说。)< / p>

因此。我写了一个装饰器来申请我的观点。装饰器将各种引发的异常类型转换为不同的返回django.http.HttpResponseXXX值。 e.g:

# This might be raised by your business logic or database code, if they get
# called with parameters that turn out to be invalid. The database code needs
# know nothing about http to do this. It might be best to define these exception
# types in a module of their own to prevent cycles, because many modules 
# might need to import them.
class InvalidData(Exception):
    pass

# This decorator is defined in the view module, and it knows to convert
# InvalidData exceptions to http status 400. Add whatever other exception types
# and http return values you need. We end with a 'catch-all' conversion of
# Exception into http 500.
def exceptions_to_http_status(view_func):
    @wraps(view_func)
    def inner(*args, **kwargs):
        try:
            return view_func(*args, **kwargs)
        except InvalidData as e:
            return django.http.HttpResponseBadRequest(str(e))   
        except Exception as e:
            return django.http.HttpResponseServerError(str(e))
     return inner

 # Then finally we define our view, using the decorator.

 @exceptions_to_http_status
 def myview(self, request):
     # The view parses what we need out of incoming requests
     data = request.GET['somearg']

     # Here in the middle of your view, delegate to your business logic,
     # which can just raise exceptions if there is an error.
     result = myusecase(data)

     # and finally the view constructs responses
     return HttpResponse(result.summary)

根据具体情况,您可能会发现相同的装饰器可能会对您的许多或所有视图功能起作用。

答案 3 :(得分:5)

HttpResponseBadRequest已准备就绪。 It is implemented as:

class HttpResponseBadRequest(HttpResponse):
    status_code = 400

已编辑由于OP更新了问题。

您可以创建自己的帮助程序并将try-catch块封装到其中。

def myJsonDec(str):
    try:
        ...

答案 4 :(得分:0)

我不确定将BadRequest作为例外提出意思。

您可以通过显式使用HttpResponse的相关子类,或通过向正常响应添加status参数来返回您喜欢的任何状态代码的响应。

答案 5 :(得分:0)

我认为一种简单的方法是定义您自己的 BadRequestException 并在被调用的函数中引发它。

from django.http import HttpResponseBadRequest, HttpResponse

class BadRequestException(Exception):
    def __init__(self, message='', *args, **kwargs):
        self.message = message

def my_view(request):
    try:
        data = get_data_from_another_func()
    except BadRequestException as e:
        return HttpResponseBadRequest(e.message)
    process_data(data)
    return HttpResponse('Thank you')

def get_data_from_another_func():
    raise BadRequestException(message='something wrong')

def process_data(data):
    pass