3

非常に単純な OData v4 コントローラーを作成しました。Petコントローラーには基本的に、次のエンティティの Entity Framework に基づく CRUD メソッドが含まれています。

public class Pet
{
    public int Id { get; set; }

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

    public int Age { get; set; }
}

ここで重要なことPet.Ageは、必須プロパティが null 非許容であることです。

コントローラー自体は次のとおりです (Postメソッドのみが示されています)。

public class PetController : ODataController
{
    private DatabaseContext db = new DatabaseContext();

    // POST: odata/Pet
    public IHttpActionResult Post(Pet pet)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        db.Pet.Add(pet);
        db.SaveChanges();

        return Created(pet);
    }

    // Other controller methods go here...
}

そして、これは私のWebApiConfigコントローラー構成です:

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Pet>("Pet");
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());

データベースに新しいものを作成したい場合は、次のようなリクエストPetを発行します。POST

POST http://localhost:8080/odata/Pet
Content-type: application/json

{ Name: "Cat", Age: 5 }

ただし、JSON 要求ペイロードでプロパティを単純に省略できるAgeため、JSON デシリアライザーは既定値の を使用しますが0400 Bad Request代わりにステータスを返す必要があります。この問題はアンダーポスティングと呼ばれます。

通常の WebApi コントローラーを使用すると、簡単に解決できます (解決策については、こちらで説明しています)。を作成し、コントローラーが実際のエンティティPetViewModelの代わりに を受け入れるようにするだけです。PetViewModelPet

public class PetViewModel
{
    // Make the property nullable and set the Required attribute
    // to distinguish between "zero" and "not set"
    [Required]
    public int? Age { get; set; }

    // Other properties go here...
}

次に、コントローラーでエンティティに変換PetViewModelPet、通常どおりデータベースに保存します。

残念ながら、このアプローチは OData コントローラーでは機能しません。メソッドをの代わりにPostacceptに変更すると、次のエラーが表示されます。PetViewModelPet

System.Net.Http.UnsupportedMediaTypeException: MediaTypeFormatter を使用して、メディア タイプ 'application/json' のコンテンツからタイプ 'PetViewModel' のオブジェクトを読み取ることができません。

System.Net.Http.HttpContentExtensions.ReadAsAsync [T] (HttpContent コンテンツ、型の種類、IEnumerable'1 フォーマッター、IFormatterLogger formatterLogger、CancellationToken cancelToken) で

System.Net.Http.HttpContentExtensions.ReadAsAsync (HttpContent コンテンツ、型の種類、IEnumerable'1 フォーマッター、IFormatterLogger formatterLogger、CancellationToken cancelToken) で

System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync (HttpRequestMessage 要求、Type タイプ、IEnumerable`1 フォーマッター、IFormatterLogger formatterLogger、CancellationToken cancelToken) で

では、OData コントローラーを使用するときに過少投稿を防ぐ方法はありますか?

4

2 に答える 2

2

いくつかの調査の後、私はこの問題を解決しました。それが「公式」またはODataのアンダーポスティングの問題を解決するための推奨される方法であるかどうかはわかりませんが、少なくとも私にとってはうまくいきます. したがって、公式情報がないため、ここに私のレシピがあります:

まず、ViewModelOData エンティティに対応する検証を作成します。

public class PetViewModel
{
    public int Id { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    // Make the property nullable and set the Required attribute
    // to distinguish between "zero" and "not set"
    [Required]
    public new int? Age { get; set; }
}

次に、独自の を追加しますODataUnderpostingValidationAttribute。私の実装は次のようになります。

public class ODataUnderpostingValidationAttribute: ActionFilterAttribute
{
    public ODataUnderpostingValidationAttribute(Type viewModelType)
    {
        ViewModelType = viewModelType;
    }

    public Type ViewModelType { get; set; }

    public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        // Rewind requestStream so it can be read again.
        var requestStream = await actionContext.Request.Content.ReadAsStreamAsync();
        if (requestStream.CanSeek)
        {
            requestStream.Position = 0;
        }

        // Read the actual JSON payload.
        var json = await actionContext.Request.Content.ReadAsStringAsync();

        // Deserialize JSON to corresponding validation ViewModel.
        var viewModel = JsonConvert.DeserializeObject(json, ViewModelType);
        var context = new ValidationContext(viewModel);
        var results = new List<ValidationResult>();
        var isValid = Validator.TryValidateObject(viewModel, context, results);

        if (!isValid)
        {
            // Throw HttpResponseException instead of setting actionContext.Response, so the exception will be logged by the ExceptionLogger.
            var responseMessage = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage)));
            throw new HttpResponseException(responseMessage);
        }

        await base.OnActionExecutingAsync(actionContext, cancellationToken);
    }
}

その後、このカスタム フィルターを に適用しますODataController

[ODataUnderpostingValidation(typeof(PetViewModel))]
public class PetController : ODataController
{ /* Implementation here */ }

出来上がり!これで、すべてが整いました。過小評価の検証は正常に機能します。

于 2016-01-23T13:24:55.830 に答える
0

私が見ているように、いくつかのオプションがあります。

最初にコントローラーで整数値を確認し、特定の値を下回る場合は 404 を返します。

if (Age <= 0)
   return NotFound();

これは労働集約的である可能性があり、すべてのコントローラーメソッドに対して実行している場合、それほど DRY ではありません。

Pet クラスの 2 番目に、DataAnnotations Attribute Range を使用できます。

[Range(0, 80, ErrorMessage = "Value for {0} must be between {1} and {2}")]
public int Age { get; set; }

Age の最大値は 80 です 。 https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.rangeattribute(v=vs.110).aspx

最後に、より永続的な解決策は、独自の検証を作成することだと思います。

public class AgeValidation : ValidationAttribute {
public override bool IsValid(object value) {
    if (Object.Equals(value, null)) {
        return false;
    }
    int getage;
    if (int.TryParse(value.ToString(), out getage)) {

        if (getage == 0)
            return false;

        if (getage > 0)
            return true;
    }
    return false;
}

}

次に、 Pet クラスに次を追加します。

[AgeValidation(ErrorMessage = "Age is wack")]
public int Age { get; set; }

How to do Integer model validation in asp.net mvc 2から借用

于 2016-01-20T18:04:51.923 に答える