“O usuário pode fazer X se o usuário possui o objeto Y”: Implementar lógica na validação do modelo ou lógica do controlador?

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

  •  27-10-2019
  •  | 
  •  

Pergunta

Considere, por exemplo, a lógica "Um usuário só pode editar ou excluir um comentário de sua autoria".

Minhas ações do controlador repetirão a lógica de verificar se o usuário atualmente conectado pode afetar o comentário.Exemplo

[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);
}

Claro, posso agrupar essa lógica em um método para não copiar / colar esse snippet;entretanto, tirar esse código do controlador e colocá-lo em um ValidationAttribute mantém meu Action menor e mais fácil de escrever testes.Exemplo

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; }
}

A validação de modelo é a ferramenta certa para este trabalho?Gosto de tirar essa preocupação da Ação do controlador;mas, neste caso, pode complicar ainda mais as coisas.Isso é especialmente verdadeiro quando você considera que esta ação é parte de uma API RESTful e precisa retornar um código de status HTTP diferente, dependendo dos erros de validação no ModelState.

Há "prática recomendada" neste caso?

Foi útil?

Solução

Pessoalmente, acho que parece bom, mas você está se deixando levar pelas anotações.Acho que isso não pertence à sua camada de apresentação e deve ser tratado por sua camada de serviço.

Eu teria algo como:

[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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top