Validação do modelo na API Web ASP.NETModel Validation in ASP.NET Web API

por Mike Wassonby Mike Wasson

Este artigo mostra como anotar seus modelos, use as anotações para validação de dados e tratar erros de validação em sua API da web.This article shows how to annotate your models, use the annotations for data validation, and handle validation errors in your web API. Quando um cliente envia dados para sua API da web, muitas vezes você deseja validar os dados antes de fazer qualquer processamento.When a client sends data to your web API, often you want to validate the data before doing any processing.

Anotações de dadosData Annotations

Na API Web ASP.NET, você pode usar atributos do DataAnnotations namespace para definir regras de validação para propriedades em seu modelo.In ASP.NET Web API, you can use attributes from the System.ComponentModel.DataAnnotations namespace to set validation rules for properties on your model. Considere o modelo a seguir:Consider the following 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; }
    }
}

Se você tiver usado a validação do modelo no ASP.NET MVC, isso deve ser familiar.If you have used model validation in ASP.NET MVC, this should look familiar. O necessária atributo diz que o Name propriedade não pode ser nula.The Required attribute says that the Name property must not be null. O intervalo atributo diz que Weight deve estar entre zero e 999.The Range attribute says that Weight must be between zero and 999.

Suponha que um cliente envia uma solicitação POST com a seguinte representação JSON:Suppose that a client sends a POST request with the following JSON representation:

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

Você pode ver que o cliente não incluiu o Name propriedade, que é marcada como obrigatório.You can see that the client did not include the Name property, which is marked as required. Quando a API da Web converte o JSON em uma Product instância, ele valida o Product contra os atributos de validação.When Web API converts the JSON into a Product instance, it validates the Product against the validation attributes. A ação de controlador, você pode verificar se o modelo é válido:In your controller action, you can check whether the model is valid:

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);
            }
        }
    }
}

Validação de modelo não garante que os dados de cliente estão seguros.Model validation does not guarantee that client data is safe. Validação adicional pode ser necessário em outras camadas do aplicativo.Additional validation might be needed in other layers of the application. (Por exemplo, a camada de dados pode impor restrições de chave estrangeira.) O tutorial usando a API da Web com o Entity Framework explora alguns desses problemas.(For example, the data layer might enforce foreign key constraints.) The tutorial Using Web API with Entity Framework explores some of these issues.

"Under-Posting": Corrigiremos lançamento ocorre quando o cliente omite algumas propriedades."Under-Posting": Under-posting happens when the client leaves out some properties. Por exemplo, suponha que o cliente envia o seguinte:For example, suppose the client sends the following:

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

Aqui, o cliente não especificou valores para Price ou Weight.Here, the client did not specify values for Price or Weight. O formatador JSON atribui um valor padrão de zero para as propriedades ausentes.The JSON formatter assigns a default value of zero to the missing properties.

O estado do modelo é válido, porque o zero é um valor válido para essas propriedades.The model state is valid, because zero is a valid value for these properties. Se esse é um problema depende do seu cenário.Whether this is a problem depends on your scenario. Por exemplo, em uma operação de atualização, convém distinguir entre "zero" e "não definido".For example, in an update operation, you might want to distinguish between "zero" and "not set." Para forçar os clientes para definir um valor, verifique a propriedade anulável e defina as necessário atributo:To force clients to set a value, make the property nullable and set the Required attribute:

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

"Over-Posting": Um cliente pode também enviar mais dados que o esperado."Over-Posting": A client can also send more data than you expected. Por exemplo:For example:

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

Aqui, o JSON inclui uma propriedade ("Color") que não existe no Product modelo.Here, the JSON includes a property ("Color") that does not exist in the Product model. Nesse caso, o formatador JSON simplesmente ignora esse valor.In this case, the JSON formatter simply ignores this value. (O formatador XML faz o mesmo.) Excesso de postagem causa problemas se seu modelo tem propriedades que devem ser somente leitura.(The XML formatter does the same.) Over-posting causes problems if your model has properties that you intended to be read-only. Por exemplo:For example:

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

Você não deseja que os usuários atualizem o IsAdmin propriedade e elevar a mesmos para os administradores!You don't want users to update the IsAdmin property and elevate themselves to administrators! A estratégia mais segura é usar uma classe de modelo que corresponda exatamente o que o cliente tem permissão para enviar:The safest strategy is to use a model class that exactly matches what the client is allowed to send:

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

Note

Postagem de blog de Brad Wilson "vs de validação de entrada. Modelo de validação no ASP.NET MVC"tem uma boa discussão do lançamento insuficiente e excesso de postagem.Brad Wilson's blog post "Input Validation vs. Model Validation in ASP.NET MVC" has a good discussion of under-posting and over-posting. Embora a postagem é sobre o ASP.NET MVC 2, os problemas ainda são relevantes para a API Web.Although the post is about ASP.NET MVC 2, the issues are still relevant to Web API.

Tratamento de erros de validaçãoHandling Validation Errors

API da Web não automaticamente retornar um erro ao cliente quando a validação falha.Web API does not automatically return an error to the client when validation fails. Cabe a ação do controlador para verificar o estado do modelo e responder adequadamente.It is up to the controller action to check the model state and respond appropriately.

Você também pode criar um filtro de ação para verificar o estado do modelo antes que a ação do controlador seja invocada.You can also create an action filter to check the model state before the controller action is invoked. O código a seguir mostra um exemplo:The following code shows an example:

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);
            }
        }
    }
}

Se a validação do modelo falhar, esse filtro retorna uma resposta HTTP que contém os erros de validação.If model validation fails, this filter returns an HTTP response that contains the validation errors. Nesse caso, a ação do controlador não é invocada.In that case, the controller action is not invoked.

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."
    ]
  }
}

Para aplicar esse filtro para todos os controladores de API da Web, adicione uma instância do filtro para o HttpConfiguration.Filters coleção durante a configuração:To apply this filter to all Web API controllers, add an instance of the filter to the HttpConfiguration.Filters collection during configuration:

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

        // ...
    }
}

Outra opção é definir o filtro como um atributo em controladores ou ações do controlador individuais:Another option is to set the filter as an attribute on individual controllers or controller actions:

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