web-dev-qa-db-ja.com

Web API.Coreでは、すべてのメソッドでモデル検証を行う方法は?

Web APIでASP.Net Core 2.0を使用しています。

私の最初の方法の1つはログインです:

/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data)
{
    var token = _manager.ValidateCredentialsAndGenerateToken(data);
    if (token == null)
    {
        return Unauthorized();
    }
    else
    {
        return Ok(token);
    }
}

DataAnnotationsを使用するLoginData

public class LoginData
{
    [Required]
    [MaxLength(50)]
    public string Username { get; set; }

    [Required]
    public string Password { get; set; }

    [Required]
    [MaxLength(16)]
    public string IpAddress { get; set; }
}

したがって、ログインが発生すると、私のModelStateは自動的に適切に入力されます。パスワードは空です(もちろん、クライアント側でも後で検証する必要があります)。

私の質問は:
a)モデルの状態を確認し、b)すべてのエラーから読み取り可能な文字列を取得し、C)このエラーでBadRequestを返す最良の方法は何ですか?

もちろん、ヘルパーメソッドですべて自分で書くこともできます。しかし、フィルターについて考えたのでしょうか。

9
Kovu

モデルの状態を確認する方法は?

アクションでコントローラーのModelStateを確認して、モデルの状態を取得します。

すべてのエラーから読み取り可能な文字列を取得し、このエラーでBadRequestを返しますか?

BadRequest(ModelState)を使用して、モデルの状態を検査し、エラーを使用してメッセージを作成するHTTP bad request responseを返します。

完成したコード

/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data) {
    if(ModelState.IsValid) {
        var token = _manager.ValidateCredentialsAndGenerateToken(data);
        if (token == null) {
            return Unauthorized();
        } else {
            return Ok(token);
        }
    }
    return BadRequest(ModelState);
}

もちろん、ヘルパーメソッドですべて自分で書くこともできます。しかし、フィルターについて考えたのでしょうか。

繰り返されるModelState.IsValidモデルの検証が必要なすべてのアクションのコードでは、フィルターを作成してモデルの状態を確認し、リクエストを短絡できます。

例えば

public class ValidateModelAttribute : ActionFilterAttribute {
    public override void OnActionExecuting(ActionExecutingContext context) {
        if (!context.ModelState.IsValid) {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

アクションに直接適用できます

[ValidateModel] //<-- validation
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data) {
    var token = _manager.ValidateCredentialsAndGenerateToken(data);
    if (token == null) {
        return Unauthorized();
    } else {
        return Ok(token);
    }    
}

または、モデルの状態を確認する必要があるすべての要求に適用されるグローバルに追加されます。

参照 ASP.NET Core MVCでのモデル検証

18
Nkosi

[ApiController]およびWeb APIベースのプロジェクトでの検証を容易にするその他の属性を使用することを強くお勧めします。

[ApiController]この属性は、メソッドに入る前にモーダルのすべての基本的な検証を行います。したがって、何らかの形式のカスタム検証を行いたい場合にのみ、モーダルを検査する必要があります。

5
Hawkzey

モデルの状態が有効かどうかを確認するには、ModelStateプロパティを使用します(Controllerクラスが継承するControllerBaseクラスによって公開されます)

ModelState.IsValid

ModelStateからエラーを取得するには、ディクショナリからエラーを除外し、それらをリストとして返すことができます

var errors = ModelState
    .Where(a => a.Value.Errors.Count > 0)
    .SelectMany(x => x.Value.Errors)
    .ToList();

1つのオプションは、すべてのメソッド/コントローラーで状態を検証することですが、モデルを検証する基本クラスで検証を実装することをお勧めします
このようなOnActionExecutingメソッド

public class ApiController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!ModelState.IsValid)
        {
            var errors = ModelState
                .Where(a => a.Value.Errors.Count > 0)
                .SelectMany(x => x.Value.Errors)
                .ToList();
            context.Result = new BadRequestObjectResult(errors);
        }
        base.OnActionExecuting(context);
    }
}

その後、自動モデル状態検証を行う必要があるすべてのコントローラーは、基本クラスから継承するだけです

public class TokenController : ApiController
{
    /// <summary>
    /// API endpoint to login a user
    /// </summary>
    /// <param name="data">The login data</param>
    /// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
    [AllowAnonymous]
    [Route("login")]
    [HttpPost]
    public IActionResult Login([FromBody]LoginData data)
    {
        var token = _manager.ValidateCredentialsAndGenerateToken(data);
        if (token == null)
        {
            return Unauthorized();
        }
        else
        {
            return Ok(token);
        }
    }
}
2
Marcus Höglund