Python django:作为异常引发BadRequest?

Python django:作为异常引发BadRequest?,python,django,exception-handling,httpresponse,Python,Django,Exception Handling,Httpresponse,是否可以在django中将BadRequest作为异常引发 我知道你可以提出404[1] 用例:在助手方法中,我从request.GET加载json。如果json是因为浏览器(IE)剪切了url而被剪切的,我想提出一个匹配的异常 BadRequest异常看起来很合适,但到目前为止,django中似乎没有这样的异常 在1.6中有一个可疑的操作异常。但这与我的情况不符,因为它与安全无关 当然,我可以试试..除了在视图方法中我的helper方法周围,但这不是干的 是否有人提出了一个解决方案,在我的助手

是否可以在django中将
BadRequest
作为异常引发

我知道你可以提出404[1]

用例:在助手方法中,我从request.GET加载json。如果json是因为浏览器(IE)剪切了url而被剪切的,我想提出一个匹配的异常

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

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

当然,我可以试试..除了在视图方法中我的helper方法周围,但这不是干的

是否有人提出了一个解决方案,在我的助手方法的每次调用中都不需要尝试..异常

[1]

更新

代码示例:

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

您需要定制中间件来处理您提出的异常。 利用自定义异常检查中间件中的这种情况

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")

我不知道你把BadRequest作为例外是什么意思

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

已准备就绪。作为:

已编辑到期OP更新问题

您可以创建自己的助手,并将try-catch块封装到其中

def myJsonDec(str):
    try:
        ...

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

如果你想挂接到Django的,你可以引发一个异常或者它的一个子类

请参阅文档和

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

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

作为@coldmind回答(在中间件层转换异常)的替代方法,您可以在视图函数上放置一个decorator,它也可以做同样的事情。就我个人而言,我更喜欢这个,因为它只是一个简单的老Python,不需要我对Django中间件的工作原理一窍不通

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

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

处理这一问题的自然方法是使用异常,但Django视图本身不会将未捕获的异常转换为返回的HTTP状态代码(如OP所述,有几个特殊情况除外)

所以。我写了一个装饰者来应用我的观点。装饰程序将各种引发的异常类型转换为不同的返回的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)

根据具体情况,您可能会发现同一个装饰器可以处理许多或所有视图函数。

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

从django.http导入HttpResponseBadRequest,HttpResponse
类BadRequestException(异常):
定义初始化(self,message='',*args,**kwargs):
self.message=消息
定义我的视图(请求):
尝试:
数据=从另一个函数()获取数据
除BadRequestException外,如e:
返回HttpResponseBadRequest(e.message)
过程数据(数据)
返回HttpResponse('谢谢')
def从另一个函数()获取数据:
引发BadRequestException(消息='something Error')
def过程_数据(数据):
通过

为什么不直接返回
django.http.HttpResponseBadRequest
?如果你想保持干燥,让你的助手方法返回适当的
HttpResponse
类,或者在其周围创建另一个快捷方式,比如
load\u json\u或\u bad\u request()
@ambi我不想返回
django.http.HttpResponseBadRequest
,因为我的方法返回解析的数据,除了输入数据被破坏之外。@guettli更新后,我认为您应该引发一个自定义异常,并按照coldmind的建议在中间件中处理它。这与
get\u object\u或\u 404使用的机制相同,我更新了这个问题。请看代码示例。HttpResponseBadRequest异常非常好,但是开箱即用的Django并没有以这种方式概括处理其他HTTP状态值。(也许他们很难做到这一点——将异常类型映射到http状态代码可能是特定于应用程序的,除非您只想为每个http状态代码显式创建一个异常类型,这意味着您的业务逻辑和它所委托的代码将充满关于http状态代码的知识。)我在这页下面的某个地方有一个答案,试图解决这个问题,但这是一堆代码。HttpResponseBadRequest不是一个例外,它不能被提出。我添加了一个代码示例。我希望现在更清楚。我不想查看函数返回类型,而是基于返回响应良好的点,记住注释行是不正确的,因为您在任何情况下都返回响应(以相同的方式),而不仅仅是在“您的异常”情况下。这可能是完美的,也可能是错误的,这取决于具体情况,我只是对注释行进行了澄清,因此这适用于<1.10版本,请参见:我认为这里的总体模式很好,但是
# 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)