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
к нормальному ответу.