Habilitando operações CRUD no ASP.NET Web API 1

por Mike Wasson

Baixar Projeto Concluído

Este tutorial mostra como dar suporte a operações CRUD em um serviço HTTP usando ASP.NET Web API para ASP.NET 4.x.

Versões de software usadas no tutorial

  • Visual Studio 2012
  • API Web 1 (também funciona com a API Web 2)

CRUD significa "Criar, Ler, Atualizar e Excluir", que são as quatro operações básicas de banco de dados. Muitos serviços HTTP também modelam operações CRUD por meio de APIs REST ou REST.

Neste tutorial, você criará uma API Web muito simples para gerenciar uma lista de produtos. Cada produto conterá um nome, um preço e uma categoria (como "brinquedos" ou "hardware"), além de uma ID do produto.

A API de produtos exporá os métodos a seguir.

Ação Método HTTP URI relativo
Obter uma lista de todos os produtos GET /api/products
Obter um produto por ID GET /api/products/id
Obter um produto por categoria GET /api/products?category=category
Criar um novo produto POST /api/products
Atualizar um produto PUT /api/products/id
Excluir um produto Delete (excluir) /api/products/id

Observe que alguns dos URIs incluem a ID do produto no caminho. Por exemplo, para obter o produto cuja ID é 28, o cliente envia uma solicitação GET para http://hostname/api/products/28.

Recursos

A API de produtos define URIs para dois tipos de recursos:

Recurso URI
A lista de todos os produtos. /api/products
Um produto individual. /api/products/id

Métodos

Os quatro métodos HTTP main (GET, PUT, POST e DELETE) podem ser mapeados para operações CRUD da seguinte maneira:

  • GET recupera a representação do recurso em um URI especificado. GET não deve ter efeitos colaterais no servidor.
  • PUT atualiza um recurso em um URI especificado. PUT também pode ser usado para criar um novo recurso em um URI especificado, se o servidor permitir que os clientes especifiquem novos URIs. Para este tutorial, a API não dará suporte à criação por meio de PUT.
  • POST cria um novo recurso. O servidor atribui o URI para o novo objeto e retorna esse URI como parte da mensagem de resposta.
  • DELETE exclui um recurso em um URI especificado.

Observação: o método PUT substitui toda a entidade do produto. Ou seja, espera-se que o cliente envie uma representação completa do produto atualizado. Se você quiser dar suporte a atualizações parciais, o método PATCH será preferencial. Este tutorial não implementa PATCH.

Criar um novo projeto de API Web

Comece executando o Visual Studio e selecione Novo Projeto na página Iniciar . Ou, no menu Arquivo , selecione Novo e Projeto.

No painel Modelos , selecione Modelos Instalados e expanda o nó Visual C #. Em Visual C#, selecione Web. Na lista de modelos de projeto, selecione ASP.NET Aplicativo Web MVC 4. Nomeie o projeto como "ProductStore" e clique em OK.

Captura de tela da nova janela do projeto, mostrando as opções de menu e realçando o caminho para criar um aplicativo Web A SP dot NET M V C 4.

Na caixa de diálogo Novo projeto ASP.NET MVC 4 , selecione API Web e clique em OK.

Captura de tela do novo projeto A SP dot NET, mostrando imagens em caixa de modelos disponíveis e realçando o modelo web A P I, em azul.

Adicionar um modelo

Um modelo é um objeto que representa os dados no seu aplicativo. Em ASP.NET Web API, você pode usar objetos CLR fortemente tipados como modelos e eles serão serializados automaticamente para XML ou JSON para o cliente.

Para a API ProductStore, nossos dados consistem em produtos, portanto, criaremos uma nova classe chamada Product.

Se o Gerenciador de Soluções não estiver visível, clique no menu Exibir e selecione Gerenciador de Soluções. Em Gerenciador de Soluções, clique com o botão direito do mouse na pasta Modelos. No menu de contexto, selecione Adicionar e, em seguida, Classe. Nomeie a classe "Product".

Captura de tela do menu do gerenciador de soluções, realçando a seleção de modelos para mostrar um menu adicional para selecionar a opção adicionar classe.

Adicione as propriedades a seguir à Product classe .

namespace ProductStore.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

Adicionando um repositório

Precisamos armazenar uma coleção de produtos. É uma boa ideia separar a coleção de nossa implementação de serviço. Dessa forma, podemos alterar o repositório de backup sem reescrever a classe de serviço. Esse tipo de design é chamado de padrão de repositório . Comece definindo uma interface genérica para o repositório.

Em Gerenciador de Soluções, clique com o botão direito do mouse na pasta Modelos. Selecione Adicionar e, em seguida, novo item.

Captura de tela do menu gerenciador de soluções, que realça a opção modelos e abre um menu para adicionar um novo item.

No painel Modelos , selecione Modelos Instalados e expanda o nó C#. Em C#, selecione Código. Na lista de modelos de código, selecione Interface. Nomeie a interface como "IProductRepository".

Captura de tela do painel modelos mostrando o menu de modelos instalado, que realça as opções de código e interface em cinza.

Adicione a seguinte implementação:

namespace ProductStore.Models
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetAll();
        Product Get(int id);
        Product Add(Product item);
        void Remove(int id);
        bool Update(Product item);
    }
}

Agora, adicione outra classe à pasta Models, chamada "ProductRepository". Essa classe implementará a IProductRepository interface . Adicione a seguinte implementação:

namespace ProductStore.Models
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> products = new List<Product>();
        private int _nextId = 1;

        public ProductRepository()
        {
            Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
            Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
            Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
        }

        public IEnumerable<Product> GetAll()
        {
            return products;
        }

        public Product Get(int id)
        {
            return products.Find(p => p.Id == id);
        }

        public Product Add(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            item.Id = _nextId++;
            products.Add(item);
            return item;
        }

        public void Remove(int id)
        {
            products.RemoveAll(p => p.Id == id);
        }

        public bool Update(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            int index = products.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            products.RemoveAt(index);
            products.Add(item);
            return true;
        }
    }
}

O repositório mantém a lista na memória local. Isso é ok para um tutorial, mas em um aplicativo real, você armazenaria os dados externamente, seja um banco de dados ou no armazenamento em nuvem. O padrão do repositório facilitará a alteração da implementação posteriormente.

Adicionando um controlador de API Web

Se você trabalhou com ASP.NET MVC, já está familiarizado com os controladores. Em ASP.NET Web API, um controlador é uma classe que manipula solicitações HTTP do cliente. O assistente Novo Projeto criou dois controladores para você quando ele criou o projeto. Para vê-los, expanda a pasta Controladores em Gerenciador de Soluções.

  • HomeController é um controlador MVC ASP.NET tradicional. Ele é responsável por servir páginas HTML para o site e não está diretamente relacionado à nossa API Web.
  • ValuesController é um controlador WebAPI de exemplo.

Vá em frente e exclua ValuesController clicando com o botão direito do mouse no arquivo em Gerenciador de Soluções e selecionando Excluir. Agora, adicione um novo controlador, da seguinte maneira:

No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Controladores. Selecione Adicionar e, em seguida, selecione Controlador.

Captura de tela do menu do gerenciador de soluções, realçando a categoria controladores, que traz outro menu, realçando o caminho para adicionar um controlador.

No assistente Adicionar Controlador , nomeie o controlador como "ProductsController". Na lista suspensa Modelo , selecione Controlador de API Vazio. Clique em Adicionar.

Captura de tela da janela Adicionar controlador, mostrando o campo nome do controlador para inserir um nome e uma lista suspensa de modelos, em opções de scaffolding.

Observação

Não é necessário colocar seus controladores em uma pasta chamada Controladores. O nome da pasta não é importante; é simplesmente uma maneira conveniente de organizar seus arquivos de origem.

O assistente Adicionar Controlador criará um arquivo chamado ProductsController.cs na pasta Controladores. Se esse arquivo não estiver aberto, clique duas vezes no arquivo para abri-lo. Adicione a seguinte instrução using :

using ProductStore.Models;

Adicione um campo que contém uma instância IProductRepository .

public class ProductsController : ApiController
{
    static readonly IProductRepository repository = new ProductRepository();
}

Observação

Chamar new ProductRepository() no controlador não é o melhor design, porque ele vincula o controlador a uma implementação específica de IProductRepository. Para obter uma abordagem melhor, consulte Usando o Resolvedor de Dependência de API Web.

Obtendo um recurso

A API ProductStore exporá várias ações de "leitura" como métodos HTTP GET. Cada ação corresponderá a um método na ProductsController classe .

Ação Método HTTP URI relativo
Obter uma lista de todos os produtos GET /api/products
Obter um produto por ID GET /api/products/id
Obter um produto por categoria GET /api/products?category=category

Para obter a lista de todos os produtos, adicione este método à ProductsController classe :

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAllProducts()
    {
        return repository.GetAll();
    }
    // ....
}

O nome do método começa com "Get", portanto, por convenção, ele é mapeado para solicitações GET. Além disso, como o método não tem parâmetros, ele é mapeado para um URI que não contém um segmento de "id" no caminho.

Para obter um produto por ID, adicione este método à ProductsController classe :

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound); 
    }
    return item;
}

Esse nome de método também começa com "Get", mas o método tem um parâmetro chamado id. Esse parâmetro é mapeado para o segmento "id" do caminho do URI. A estrutura ASP.NET Web API converte automaticamente a ID para o tipo de dados correto (int) para o parâmetro .

O método GetProduct gerará uma exceção do tipo HttpResponseException se id não for válida. Essa exceção será convertida pela estrutura em um erro 404 (Não Encontrado).

Por fim, adicione um método para localizar produtos por categoria:

public IEnumerable<Product> GetProductsByCategory(string category)
{
    return repository.GetAll().Where(
        p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}

Se o URI da solicitação tiver uma cadeia de caracteres de consulta, a API Web tentará corresponder os parâmetros de consulta aos parâmetros no método do controlador. Portanto, um URI do formato "api/products?category=category" será mapeado para esse método.

Criando um recurso

Em seguida, adicionaremos um método à ProductsController classe para criar um novo produto. Aqui está uma implementação simples do método :

// Not the final implementation!
public Product PostProduct(Product item)
{
    item = repository.Add(item);
    return item;
}

Observe duas coisas sobre esse método:

  • O nome do método começa com "Post...". Para criar um novo produto, o cliente envia uma solicitação HTTP POST.
  • O método usa um parâmetro do tipo Product. Na API Web, os parâmetros com tipos complexos são desserializados do corpo da solicitação. Portanto, esperamos que o cliente envie uma representação serializada de um objeto de produto, no formato XML ou JSON.

Essa implementação funcionará, mas ainda não está completa. O ideal é que a resposta HTTP inclua o seguinte:

  • Código de resposta: Por padrão, a estrutura da API Web define a resposta status código como 200 (OK). Mas de acordo com o protocolo HTTP/1.1, quando uma solicitação POST resulta na criação de um recurso, o servidor deve responder com status 201 (Criado).
  • Localização: Quando o servidor cria um recurso, ele deve incluir o URI do novo recurso no cabeçalho Local da resposta.

ASP.NET Web API facilita a manipulação da mensagem de resposta HTTP. Aqui está a implementação aprimorada:

public HttpResponseMessage PostProduct(Product item)
{
    item = repository.Add(item);
    var response = Request.CreateResponse<Product>(HttpStatusCode.Created, item);

    string uri = Url.Link("DefaultApi", new { id = item.Id });
    response.Headers.Location = new Uri(uri);
    return response;
}

Observe que o tipo de retorno do método agora é HttpResponseMessage. Retornando um HttpResponseMessage em vez de um Produto, podemos controlar os detalhes da mensagem de resposta HTTP, incluindo o código status e o cabeçalho Location.

O método CreateResponse cria um HttpResponseMessage e grava automaticamente uma representação serializada do objeto Product no corpo da mensagem de resposta.

Observação

Este exemplo não valida o Product. Para obter informações sobre validação de modelo, consulte Validação de modelo em ASP.NET Web API.

Atualizando um recurso

Atualizar um produto com PUT é simples:

public void PutProduct(int id, Product product)
{
    product.Id = id;
    if (!repository.Update(product))
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}

O nome do método começa com "Put...", portanto, a API Web corresponde a solicitações PUT. O método usa dois parâmetros, a ID do produto e o produto atualizado. O parâmetro id é obtido do caminho do URI e o parâmetro product é desserializado do corpo da solicitação. Por padrão, a estrutura ASP.NET Web API usa tipos de parâmetro simples da rota e tipos complexos do corpo da solicitação.

Excluindo um recurso

Para excluir um recurso, defina um "Excluir..." Método.

public void DeleteProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    repository.Remove(id);
}

Se uma solicitação DELETE for bem-sucedida, ela poderá retornar status 200 (OK) com um corpo de entidade que descreve o status; status 202 (Aceito) se a exclusão ainda estiver pendente; ou status 204 (Sem Conteúdo) sem nenhum corpo de entidade. Nesse caso, o DeleteProduct método tem um void tipo de retorno, portanto, ASP.NET Web API converte automaticamente isso em status código 204 (Sem Conteúdo).