Django: вызывать BadRequest как исключение?

Можно ли поднять BadRequest как исключение в django?

Я видел, что вы можете поднять 404 [1].

Используйте случай: в вспомогательном методе я загружаю json из request.GET. Если json был вырезан, так как браузер (IE) разрезал URL-адрес, я хотел бы поднять соответствующее исключение.

Исключение BadRequest выглядит подходящим, но до сих пор в django такого исключения нет.

В 1.6 есть исключение SuspiciousOperation. Но это не совпадает в моем случае, поскольку это не связано с безопасностью.

Конечно, я мог бы попробовать try..except вокруг моего вспомогательного метода в методе view, но это не DRY.

У кого-то есть решение, в котором мне не нужно try..exception вокруг каждого вызова моего вспомогательного метода?

[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

Ответ 1

Вам нужно специальное промежуточное программное обеспечение, чтобы справиться с тем, что вы поднимаете. Для проверки его в промежуточном программном обеспечении вам понадобятся пользовательские исключения.

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

Другие ответы объясняют, как вернуть ответ HTTP с статусом 400.

Если вы хотите подключиться к обработке ошибок Django 400, вы можете создать исключение SuspiciousOperation или его подклассом.

Смотрите здесь здесь и здесь.

В вашем примере это будет выглядеть так:

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

Ответ 3

HttpResponseBadRequest готов к использованию. Он реализован как:

class HttpResponseBadRequest(HttpResponse):
    status_code = 400

Отредактировано из-за обновленного вопроса OP.

Вы можете создать свой собственный помощник и инкапсулировать блок try-catch в него.

def myJsonDec(str):
    try:
        ...

Ответ 4

Как альтернатива ответу @coldmind (преобразование исключений в слой промежуточного слоя), вы можете поместить декоратор в свою функцию просмотра, что делает то же самое. Лично я предпочитаю это, потому что он просто простой-Python, и не требует от меня пыли, когда я знаю, как работает промежуточное программное обеспечение Django.

Вы не хотите, чтобы потоковое представление включало все функции в ваших функциях просмотра (это заставляет ваш модуль просмотра зависеть от всех ваших проектов других модулей, что приводит к архитектуре "все зависит от всего остального" ). Вместо этого лучше если представление просто знает о http. Он извлекает из запроса то, что вам нужно, передает делегацию другой функции "бизнес-логики". Бизнес-логика может делегировать другие модули (например, код базы данных или интерфейсы для других внешних систем). Затем, наконец, возвращаемое значение из вашей бизнес-логики преобразуется в HTTP-ответ функцией просмотра.

Но как передавать ошибки обратно в функцию вида из бизнес-логики (или независимо от того, что она делегирует)? Использование возвращаемых значений является неприятным по многим причинам. Например, эти возвращаемые значения ошибки должны быть переданы обратно в представление из всей вашей всей базы кода. Это часто бывает бесполезным, потому что вы уже будете использовать возвращаемые значения функций для других целей.

Естественным способом справиться с этим является использование исключений, но представление Django само по себе не превратит исключенные исключения в возвращенные коды состояния HTTP (за исключением нескольких особых случаев, как говорит OP).

Итак. Я пишу декоратору, чтобы применить его к моему представлению. Декоратор преобразует различные типы исключенных типов в разные возвращаемые значения django.http.HttpResponseXXX. например:

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

В зависимости от обстоятельств вы можете обнаружить, что один и тот же декоратор может работать со многими или всеми функциями вашего представления.

Ответ 5

Я не уверен, что вы подразумеваете, создавая BadRequest в качестве исключения.

Вы можете вернуть ответ с любым кодом состояния, который вам нравится, либо явно используя соответствующий подкласс HttpResponse, либо добавив параметр status к нормальному ответу.