Ověření modelu ve webovém rozhraní API ASP.NET

Tento článek ukazuje, jak přidávat poznámky k modelům, používat poznámky k ověření dat a zpracovávat chyby ověřování ve webovém rozhraní API. Když klient odesílá data do webového rozhraní API, často je chcete před zpracováním ověřit.

Datové poznámky

V ASP.NET webovém rozhraní API můžete pomocí atributů z oboru názvů System.ComponentModel.DataAnnotations nastavit ověřovací pravidla pro vlastnosti vašeho modelu. Představte si následující model:

using System.ComponentModel.DataAnnotations;

namespace MyApi.Models
{
    public class Product
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        public decimal Price { get; set; }
        [Range(0, 999)]
        public double Weight { get; set; }
    }
}

Pokud jste v ASP.NET MVC použili ověření modelu, mělo by to vypadat povědomě. Atribut Required říká, že Name vlastnost nesmí být null. Atribut Range říká, že Weight musí být mezi nulou a 999.

Předpokládejme, že klient odešle požadavek POST s následující reprezentací JSON:

{ "Id":4, "Price":2.99, "Weight":5 }

Vidíte, že klient nezahrnoval Name vlastnost, která je označena jako povinná. Když webové rozhraní API převede JSON na Product instanci, ověří Product hodnotu proti ověřovacím atributům. V akci kontroleru můžete zkontrolovat, jestli je model platný:

using MyApi.Models;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MyApi.Controllers
{
    public class ProductsController : ApiController
    {
        public HttpResponseMessage Post(Product product)
        {
            if (ModelState.IsValid)
            {
                // Do something with the product (not shown).

                return new HttpResponseMessage(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }
    }
}

Ověření modelu nezaručuje, že jsou klientská data v bezpečí. V jiných vrstvách aplikace může být potřeba další ověření. (Datová vrstva může například vynucovat omezení cizího klíče.) Kurz Používání webového rozhraní API s Rozhraním Entity Framework se zabývá některými z těchto problémů.

"Nevyúčtovací": K podúčtovování dochází, když klient vynechá některé vlastnosti. Předpokládejme například, že klient odešle následující:

{"Id":4, "Name":"Gizmo"}

V této části klient nezadal hodnoty pro Price nebo Weight. Formátovací modul JSON přiřadí chybějícím vlastnostem výchozí hodnotu nula.

Snímek obrazovky fragmentu kódu s možnostmi rozevírací nabídky Product Store s tečkou Models tečkou Product

Stav modelu je platný, protože nula je platná hodnota pro tyto vlastnosti. To, jestli se jedná o problém, závisí na vašem scénáři. Například při operaci aktualizace můžete chtít rozlišovat mezi hodnotami "nula" a "nenastavena". Pokud chcete klientům vynutit nastavení hodnoty, nastavte vlastnost null a nastavte atribut Povinný :

[Required]
public decimal? Price { get; set; }

"Over-Posting": Klient může také odeslat více dat, než jste očekávali. Příklad:

{"Id":4, "Name":"Gizmo", "Color":"Blue"}

V tomto případě json obsahuje vlastnost ("Color"), která v Product modelu neexistuje. V tomto případě formátovací modul JSON tuto hodnotu jednoduše ignoruje. (Formátovací modul XML provede totéž.) Nadměrné účtování způsobuje problémy, pokud váš model obsahuje vlastnosti, které jste chtěli použít jen pro čtení. Příklad:

public class UserProfile
{
    public string Name { get; set; }
    public Uri Blog { get; set; }
    public bool IsAdmin { get; set; }  // uh-oh!
}

Nechcete, aby uživatelé aktualizovali IsAdmin vlastnost a zvýšili úroveň na správce. Nejbezpečnější strategií je použít třídu modelu, která přesně odpovídá tomu, co klient může odesílat:

public class UserProfileDTO
{
    public string Name { get; set; }
    public Uri Blog { get; set; }
    // Leave out "IsAdmin"
}

Poznámka

Blogový příspěvek Brada Wilsona "Input Validation vs. Model Validation in ASP.NET MVC" má dobrou diskuzi o nedostatečném publikování a nadměrném publikování. I když se příspěvek týká ASP.NET MVC 2, problémy se stále týkají webového rozhraní API.

Zpracování chyb ověřování

Webové rozhraní API při selhání ověření automaticky nevrací klientovi chybu. Kontrola stavu modelu a odpovídající reakce je na kontroleru.

Můžete také vytvořit filtr akcí pro kontrolu stavu modelu před vyvolání akce kontroleru. Podívejte se na ukázku kódu:

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Http.ModelBinding;

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

Pokud se ověření modelu nezdaří, vrátí tento filtr odpověď HTTP, která obsahuje chyby ověření. V takovém případě se akce kontroleru nevyvolá.

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Date: Tue, 16 Jul 2013 21:02:29 GMT
Content-Length: 331

{
  "Message": "The request is invalid.",
  "ModelState": {
    "product": [
      "Required property 'Name' not found in JSON. Path '', line 1, position 17."
    ],
    "product.Name": [
      "The Name field is required."
    ],
    "product.Weight": [
      "The field Weight must be between 0 and 999."
    ]
  }
}

Pokud chcete tento filtr použít na všechny kontrolery webového rozhraní API, přidejte během konfigurace do kolekce HttpConfiguration.Filters instanci filtru:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new ValidateModelAttribute());

        // ...
    }
}

Další možností je nastavit filtr jako atribut pro jednotlivé kontrolery nebo akce kontroleru:

public class ProductsController : ApiController
{
    [ValidateModel]
    public HttpResponseMessage Post(Product product)
    {
        // ...
    }
}