“El usuario puede hacer X si el usuario es propietario del objeto Y”: ¿Implementar la lógica en la validación del modelo o la lógica del controlador?

StackOverflow https://stackoverflow.com/questions/9333916

  •  27-10-2019
  •  | 
  •  

Pregunta

Considere, por ejemplo, la lógica "Un usuario solo puede editar o eliminar un comentario escrito por el usuario".

My Controller Actions repetirá la lógica de verificar si el usuario actualmente conectado puede afectar el comentario.Ejemplo

[Authorize]
public ActionResult DeleteComment(int comment_id)
{
    var comment = CommentsRepository.getCommentById(comment_id);
    if(comment == null) 
        // Cannot find comment, return bad input
        return new HttpStatusCodeResult(400); 
    if(comment.author != User.Identity.Name)
        // User not allowed to delete this comment, return Forbidden
        return new HttpStatusCodeResult(403);
    // Error checking passed, continue with delete action
    return new HttpStatusCodeResult(200);
}

Por supuesto, puedo agrupar esa lógica en un método para no copiar / pegar ese fragmento;sin embargo, sacar ese código del controlador y ponerlo en un ValidationAttribute mantiene mi acción más pequeña y más fácil de escribir pruebas.Ejemplo

public class MustBeCommentAuthorAttribute : ValidationAttribute
{
    // Import attribute for Dependency Injection
    [Import]
    ICommentRepository CommentRepository { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        int comment_id = (int)value;
        var comment = CommentsRepository.getCommentById(comment_id);
        if(comment == null) 
            return new ValidationResult("No comment with that ID"); 
        if(comment.author != HttpContext.Current.User.Identity.Name)
            return new ValidationResult("Cannot edit this comment");
        // No errors
        return ValidationResult.Success;
     }
}

public class DeleteCommentModel
{
    [MustBeCommentAuthor]
    public int comment_id { get; set; }
}

¿La validación de modelos es la herramienta adecuada para este trabajo?Me gusta sacar esa preocupación de la acción del controlador;pero en este caso, puede complicar aún más las cosas.Esto es especialmente cierto cuando considera que esta acción es parte de una API RESTful y necesita devolver un código de estado HTTP diferente dependiendo de los errores de validación en ModelState.

¿Existe una "práctica recomendada" en este caso?

¿Fue útil?

Solución

Personalmente, creo que se ve bien, pero te estás dejando llevar por las anotaciones.Creo que esto no pertenece a su capa de presentación y debería ser manejado por su capa de servicio.

Tendría algo en las líneas de:

[Authorize] 
public ActionResult DeleteComment(int comment_id) 
{ 
    try
    {
       var result = CommentsService.GetComment(comment_id, Auth.Username);

       // Show success to the user
    }
    catch(Exception e)
    {
       // Handle by displaying relevant message to the user
    }
} 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top