Saltar al contenido

Django: mejores prácticas de manejo de excepciones y envío de mensajes de error personalizados

Hola usuario de nuestra web, tenemos la solución a lo que estabas buscando, continúa leyendo y la verás más abajo.

Solución:

En primer lugar, debe pensar en qué errores desea exponer:

  • Por lo general, los errores 4xx (errores que se atribuyen al lado del cliente) se revelan para que el usuario pueda corregir la solicitud.

  • Por otro lado, los errores 5xx (errores que se atribuyen al lado del servidor) generalmente solo se presentan sin información. En mi opinión, aquellos que deberían usar herramientas como Sentry monitorean y resuelven estos errores, que pueden tener problemas de seguridad incrustados en ellos.

Teniendo esto en cuenta, en mi opinión, para una solicitud Ajax correcta, debe devolver un código de estado y luego un json para ayudar a comprender lo que sucedió, como un mensaje y una explicación (cuando corresponda).

Si su objetivo es utilizar ajax para enviar información, le sugiero que configure un formulario para lo que desea. De esta manera, superará algunos de los procesos de validación con facilidad. Asumiré que el caso es este en el ejemplo.

Primero – ¿Es correcta la solicitud?

def test_view(request):
    message = None
    explanation = None
    status_code = 500
    # First, is the request correct?
    if request.is_ajax() and request.method == "POST":
        ....
    else: 
        status_code = 400
        message = "The request is not valid."
        # You should log this error because this usually means your front end has a bug.
        # do you whant to explain anything?
        explanation = "The server could not accept your request because it was not valid. Please try again and if the error keeps happening get in contact with us."

    return JsonResponse('message':message,'explanation':explanation, status=status_code)

Segundo – ¿Hay errores en el formulario?

form = TestForm(request.POST)
if form.is_valid():
    ...
else:
    message = "The form has errors"
    explanation = form.errors.as_data()
    # Also incorrect request but this time the only flag for you should be that maybe JavaScript validation can be used.
    status_code = 400

Incluso puede obtener un error campo por campo para que pueda presentarse de una mejor manera en el formulario en sí.

Tercero – Procesemos la solicitud

        try:
            test_method(form.cleaned_data)
        except `PermissionError` as e:
            status_code= 403
            message= "Your account doesn't have permissions to go so far!"
        except `Conflict` as e:
            status_code= 409
            message= "Other user is working in the same information, he got there first"
        ....
        else:
            status_code= 201
            message= "Object created with success!"

Dependiendo de las excepciones que defina, es posible que se requieran códigos diferentes. Vaya a Wikipedia y consulte la lista. No olvide que la respuesta también varía en el código. Si agrega algo a la base de datos, debe devolver un 201. Si acaba de obtener información, entonces estaba buscando una solicitud GET.

Respondiendo a las preguntas

  1. Las excepciones de Django devolverán 500 errores si no se tratan, porque si no sabe que va a ocurrir una excepción, entonces es un error en el servidor. Con excepción del 404 y los requisitos de inicio de sesión, lo haría try catch bloques para todo. (Para 404 puede subirlo y si lo hace @login_requiredo un permiso requerido, django responderá con el código apropiado sin que usted haga nada).

  2. No estoy completamente de acuerdo con el enfoque. Como dijiste, los errores deben ser explícitos, por lo que debes saber en todo momento qué se supone que debe suceder y cómo explicarlo, y hacerlo dependiente de la operación realizada.

  3. Yo diría que un error 400 está bien para eso. Es una solicitud incorrecta, solo necesita explicar por qué, el código de error es para usted y para su código js, ​​así que sea coherente.

  4. (ejemplo proporcionado) – En el text_view deberías tener el test_method como en el tercer ejemplo.

El método de prueba debe tener la siguiente estructura:

def test_method(validated_data):
    try: 
        my_business_logic_is_violated():
    catch BusinessLogicViolation:
        raise
    else:
        ... #your code

El en mi ejemplo:

   try:
        test_method(form.cleaned_data)
    except `BusinessLogicViolation` as e:
        status_code= 400
        message= "You violated the business logic"
        explanation = e.explanation
   ...

Consideré que la violación de la lógica empresarial era un error del cliente porque si se necesita algo antes de esa solicitud, el cliente debe ser consciente de ello y pedirle al usuario que lo haga primero. (De la definición de error):

El código de estado 400 (Solicitud incorrecta) indica que el servidor no puede o no procesará la solicitud debido a algo que se percibe como un error del cliente (p. Ej., Sintaxis de solicitud mal formada, solicitud no válida
marcos de mensajes o enrutamiento de solicitudes engañosas).

Por cierto, puede ver los documentos de Python sobre excepciones definidas por el usuario para que pueda dar los mensajes de error apropiados. La idea detrás de este ejemplo es que levante un BusinessLogicViolationexcepción con un mensaje diferente en my_business_logic_is_violated()según el lugar donde se generó.

Los códigos de estado están muy bien definidos en el estándar HTTP. Puede encontrar una lista muy legible en Wikipedia. Básicamente, los errores en el rango 4XX son errores cometidos por el cliente, es decir, si solicita un recurso que no existe, etc. Los errores en el rango 5XX deben devolverse si se encuentra un error del lado del servidor.

Con respecto al punto número 3, debe elegir un error 4XX para el caso en el que no se haya cumplido una condición previa, por ejemplo 428 Precondition Required, pero devuelve un error 5XX cuando un servidor genera un error de sintaxis.

Uno de los problemas con su ejemplo es que no se devuelve ninguna respuesta a menos que el servidor genere una excepción específica, es decir, cuando el código se ejecuta normalmente y no se genera ninguna excepción, ni el mensaje ni el código de estado se envían explícitamente al cliente. Esto se puede solucionar mediante un bloque final, para que esa parte del código sea lo más genérica posible.

Según tu ejemplo:

def test_view (request):
   try:
       # Some code .... 
       status = 200
       msg = 'Everything is ok.'
       if my_business_logic_is_violated():
           # Here we're handling client side errors, and hence we return
           # status codes in the 4XX range
           status = 428
           msg = 'You violated bussiness logic because a precondition was not met'.
   except SomeException as e:
       # Here, we assume that exceptions raised are because of server
       # errors and hence we return status codes in the 5XX range
       status = 500
       msg = 'Server error, yo'
   finally:
       # Here we return the response to the client, regardless of whether
       # it was created in the try or the except block
       return JsonResponse('message': msg, status=status)

Sin embargo, como se indica en los comentarios, tendría más sentido hacer ambas validaciones de la misma manera, es decir, a través de excepciones, así:

def test_view (request):
   try:
       # Some code .... 
       status = 200
       msg = 'Everything is ok.'
       if my_business_logic_is_violated():
           raise MyPreconditionException()
   except MyPreconditionException as e:
       # Here we're handling client side errors, and hence we return
       # status codes in the 4XX range
       status = 428
       msg = 'Precondition not met.'
   except MyServerException as e:
       # Here, we assume that exceptions raised are because of server
       # errors and hence we return status codes in the 5XX range
       status = 500
       msg = 'Server error, yo.'
   finally:
       # Here we return the response to the client, regardless of whether
       # it was created in the try or the except block
       return JsonResponse('message': msg, status=status)

Aquí tienes las reseñas y calificaciones

Si para ti ha sido provechoso este post, sería de mucha ayuda si lo compartieras con otros seniors de esta forma nos ayudas a difundir nuestro contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *