web-dev-qa-db-ja.com

ASP.NET Web APIでModelState検証を処理する

ASP.NET Web APIを使用してモデル検証をどのように実現できるのか疑問に思っていました。私のようなモデルがあります:

public class Enquiry
{
    [Key]
    public int EnquiryId { get; set; }
    [Required]
    public DateTime EnquiryDate { get; set; }
    [Required]
    public string CustomerAccountNumber { get; set; }
    [Required]
    public string ContactName { get; set; }
}

その後、API ControllerにPostアクションがあります。

public void Post(Enquiry enquiry)
{
    enquiry.EnquiryDate = DateTime.Now;
    context.DaybookEnquiries.Add(enquiry);
    context.SaveChanges();
}

if(ModelState.IsValid)を追加し、エラーメッセージを処理してユーザーに渡す方法を教えてください。

97
CallumVass

懸念を分離するために、モデルの検証にアクションフィルターを使用することをお勧めします。そのため、APIコントローラーで検証を行う方法を気にする必要はありません。

using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace System.Web.Http.Filters
{
    public class ValidationActionFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var modelState = actionContext.ModelState;

            if (!modelState.IsValid)
                actionContext.Response = actionContext.Request
                     .CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
        }
    }
}
176
cuongle

このように、例えば:

public HttpResponseMessage Post(Person person)
{
    if (ModelState.IsValid)
    {
        PersonDB.Add(person);
        return Request.CreateResponse(HttpStatusCode.Created, person);
    }
    else
    {
        // the code below should probably be refactored into a GetModelErrors
        // method on your BaseApiController or something like that

        var errors = new List<string>();
        foreach (var state in ModelState)
        {
            foreach (var error in state.Value.Errors)
            {
                errors.Add(error.ErrorMessage);
            }
        }
        return Request.CreateResponse(HttpStatusCode.Forbidden, errors);
    }
}

これにより、次のような応答が返されます(JSONを想定していますが、XMLの基本原理は同じです)。

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
(some headers removed here)

["A value is required.","The field First is required.","Some custom errorm essage."]

もちろん、フィールド名、フィールドIDなどを追加するなど、好きな方法でエラーオブジェクト/リストを作成できます。

新しいエンティティのPOSTのような「一方向」のAjax呼び出しであっても、呼び出し元に何かを返す必要があります。これは、リクエストが成功したかどうかを示すものです。ユーザーがAJAX POSTリクエストを介して自分自身に関する情報を追加するサイトを想像してください。入力しようとした情報が無効な場合-保存アクションが成功したかどうかをどのように知るのでしょうか?

これを行う最善の方法は、200 OKなどのGood Old HTTP Status Codesを使用することです。このようにして、JavaScriptは正しいコールバック(エラー、成功など)を使用して失敗を適切に処理できます。

ActionFilterとjQueryを使用した、このメソッドのより高度なバージョンに関する素敵なチュートリアルを次に示します。 http://asp.net/web-api/videos/getting-started/custom-validation

28
Anders Arpi

たぶんあなたが探していたものではありませんが、おそらく誰かが知っているニース:

.net Web Api 2を使用している場合は、次のことができます。

if (!ModelState.IsValid)
     return BadRequest(ModelState);

モデルのエラーに応じて、次の結果が得られます。

{
   Message: "The request is invalid."
   ModelState: {
       model.PropertyA: [
            "The PropertyA field is required."
       ],
       model.PropertyB: [
             "The PropertyB field is required."
       ]
   }
}
24
Are Almaas
9
Lijo

または、アプリのエラーの単純なコレクションを探している場合..これは私の実装です:

public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var modelState = actionContext.ModelState;

        if (!modelState.IsValid) 
        {

            var errors = new List<string>();
            foreach (var state in modelState)
            {
                foreach (var error in state.Value.Errors)
                {
                    errors.Add(error.ErrorMessage);
                }
            }

            var response = new { errors = errors };

            actionContext.Response = actionContext.Request
                .CreateResponse(HttpStatusCode.BadRequest, response, JsonMediaTypeFormatter.DefaultMediaType);
        }
    }

エラーメッセージの応答は次のようになります。

{
  "errors": [
    "Please enter a valid phone number (7+ more digits)",
    "Please enter a valid e-mail address"
  ]
}
7

C#

    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ModelState.IsValid == false)
            {
                actionContext.Response = actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }

...

    [ValidateModel]
    public HttpResponseMessage Post([FromBody]AnyModel model)
    {

Javascript

$.ajax({
        type: "POST",
        url: "/api/xxxxx",
        async: 'false',
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(data),
        error: function (xhr, status, err) {
            if (xhr.status == 400) {
                DisplayModelStateErrors(xhr.responseJSON.ModelState);
            }
        },
....


function DisplayModelStateErrors(modelState) {
    var message = "";
    var propStrings = Object.keys(modelState);

    $.each(propStrings, function (i, propString) {
        var propErrors = modelState[propString];
        $.each(propErrors, function (j, propError) {
            message += propError;
        });
        message += "\n";
    });

    alert(message);
};
2
Nick Hermans

ここで、モデルの状態エラーを1つずつ表示するように確認できます

 public HttpResponseMessage CertificateUpload(employeeModel emp)
    {
        if (!ModelState.IsValid)
        {
            string errordetails = "";
            var errors = new List<string>();
            foreach (var state in ModelState)
            {
                foreach (var error in state.Value.Errors)
                {
                    string p = error.ErrorMessage;
                    errordetails = errordetails + error.ErrorMessage;

                }
            }
            Dictionary<string, object> dict = new Dictionary<string, object>();



            dict.Add("error", errordetails);
            return Request.CreateResponse(HttpStatusCode.BadRequest, dict);


        }
        else
        {
      //do something
        }
        }

}

2
Debendra Dash

特定のモデルオブジェクトのactionContext.ModelState.IsValidに対してModelStateFilterが常にfalse(およびその後400)を返す accepted solution pattern の実装に問題がありました。

public class ModelStateFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest};
        }
    }
}

JSONのみを受け入れるため、カスタムモデルバインダークラスを実装しました。

public class AddressModelBinder : System.Web.Http.ModelBinding.IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext)
    {
        var posted = actionContext.Request.Content.ReadAsStringAsync().Result;
        AddressDTO address = JsonConvert.DeserializeObject<AddressDTO>(posted);
        if (address != null)
        {
            // moar val here
            bindingContext.Model = address;
            return true;
        }
        return false;
    }
}

モデルの後に直接登録する

config.BindParameter(typeof(AddressDTO), new AddressModelBinder());
1
user326608

ここに記載されているように、例外をスローすることもできます。 http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx =

注意、その記事が示唆することを行うには、System.Net.Httpを含めることを忘れないでください

1