たとえば、「ユーザーは、ユーザーが作成したコメントのみを編集または削除できる」というロジックを考えてみてください。
My Controller Actionsは、現在ログインしているユーザーがコメントに影響を与える可能性があるかどうかを確認するロジックを繰り返します。例
[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);
}
もちろん、そのスニペットをコピー/貼り付けしないように、そのロジックをメソッドにバンドルすることもできます。ただし、そのコードをコントローラーから取り出してValidationAttributeに入れると、アクションが小さくなり、テストを記述しやすくなります。例
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; }
}
モデル検証はこの仕事に適したツールですか?私はその懸念をコントローラーのアクションから取り除くのが好きです。しかし、この場合、事態はさらに複雑になる可能性があります。これは、このアクションがRESTful APIの一部であり、ModelStateの検証エラーに応じて異なるHTTPステータスコードを返す必要があると考える場合に特に当てはまります。
この場合の「ベストプラクティス」はありますか?