Baseado no artigo: http://www.asp.net/web-api/overview/creating-web-apis/creating-a-web-api-that-supports-crud-operations
***
Se você não conhece nada sobre o recurso Web API da ASP .NET MVC 4, sugiro que leia os seguintes artigos:
- ASP.NET MVC 4 – Apresentação;
- ASP.NET MVC 4 – Usando Web API;
- ASP.NET MVC 4 – Usando Web API – JavaScript e jQuery – II.
Neste artigo eu vou mostrar como dar apoio a operações CRUD (Create, Reate, Update e Delete) em um serviço de HTTP usando a ASP.NET Web API.
A palavra CRUD é um acrônimo que significa “Create, Read, Udpate e Delete”, que são as quatro operações básicas de um banco de dados. Muitos serviços HTTP também modelam operações CRUD através de REST ou REST-like APIs.
Neste artigo, você vai construir uma Web API muito simples para gerenciar uma lista de produtos. Cada produto conterá um nome, preço e categoria (como “papelaria” ou “informática”), além de uma identificação do produto.
A nossa API produtos irá expor os seguintes métodos:
Ação | Método HTTP | URI relativa |
Obter uma lista de produtos | GET | /api/produtos |
Obter um produto pelo seu ID | GET | /api/produtos/id |
Obter os produtos pela sua categoria | GET | /api/produtos/?categoria=categoria |
Criar um novo produto | POST | /api/produtos |
Atualizar um produto | PUT | /api/produtos/id |
Deletar um produto | DELETE | /api/produtos/id |
Observe que algumas das URIs incluem a identificação do produto no caminho. Por exemplo, para obter o produto cuja identificação é de 28, o cliente envia uma solicitação GET para http://hostname/api/produtos/28.
Os quatro principais métodos HTTP (GET, PUT, POST e DELETE) podem ser mapeados para operações CRUD, como mostrado a seguir:
- – retorna a representação do recurso em uma URI especificada. O GET não deve ter efeitos colaterais sobre o servidor;
- PUT – atualiza um recurso em uma URI especificada. O PUT também pode ser usado para criar um novo recurso em uma URI especificada, se o servidor permitir que os clientes especifiquem novo URIs;
- POST – cria um novo recurso. O servidor atribui a URI para o novo objeto e retorna esta URI como parte da mensagem de resposta.
- DELETE – exclui um recurso em uma URI especificada.
Eu vou utilizar o Visual Web Developer 2010 Express Edition ou Visual Studio 2010 – vcê tem que instalar o recurso ASP .NET MVC4.
Criando uma nova Web API
Abra, então, o Visual Web Developer 2010 Express Edition e, no menu File, clique em New Project;
A seguir selecione a linguagem Visual Basic e clique em web a selecione o template ASP .NET MVC 4 Web Application, informando o nome ProdutosRepositorio e clicando no botão OK;
Na janela de diálogo New ASP.NET MVC 4 Project, selecione o template Web API, o view Engine Razor e clique no botão OK;
Será criado um projeto com uma estrutura já pronta e contendo alguns recursos definidos.
Definindo o Model
Um model ou modelo é um objeto que representa os dados do nosso aplicativo. A ASP.NET Web API pode serializar automaticamente o seu modelo para JSON, XML, ou outro formato, e depois gravar os dados serializados no corpo da mensagem de resposta HTTP. Enquanto um cliente pode ler o formato de serialização e pode também desserializar o objeto. Além disso, muitos clientes podem analisar o XML ou o JSON e também podem indicar qual o formato que desejam tratar, definindo o cabeçalho Accept na mensagem de solicitação HTTP.
Clique com o botão direito sobre a pasta Models e selecione a opção Add e a seguir Class. A seguir, informe o nome da classe como Produto.vb e clique no botão Add. Digite o código abaixo neste arquivo:
Public Class Produto Public Property Id As Integer Public Property Nome As String Public Property Categoria As String Public Property Preco As Decimal End Class
Incluindo um repositório
Precisamos armazenar uma coleção de produtos. É uma boa ideia separar a coleção da nossa implementação do serviço. Dessa forma, podemos alterar o armazenamento de backup sem reescrever a classe de serviço. Este tipo de projeto é chamado padrão de repositório. Vamos iniciar definindo uma interface genérica para o repositório.
Clique com o botão direito sobre a pasta Models e selecione a opção Add e a seguir New Item. Na janela Add New Item, selecione o template Interface e informe o nome IProdutoRepositorio.vb e clique no botão Add.
A seguir, digite o código abaixo para implementar a interface:
Public Interface IProdutoRepositorio Function GetTodos() As IEnumerable(Of Produto) Function GetPorID(id As Integer) As Produto Function Adicionar(item As Produto) As Produto Sub Remover(id As Integer) Function Atualizar(item As Produto) As Boolean End Interface
O próximo passo será incluir uma classe chamada ProdutoRepositorio que implementar a interface que acabamos de criar.
Clique com o botão direito sobre a pasta Models e selecione a opção Add e a seguir Class. A seguir, informe o nome da classe como Produto.vb e clique no botão Add e digite o código abaixo neste arquivo:
Public Class ProdutoRepositorio Implements IProdutoRepositorio Private produtos As New List(Of Produto)() Private _proximoId As Integer = 1 Public Sub New() Adicionar(New Produto() With { _ .Id = 1, .Nome = "Caneta", .Categoria = "Papelaria", .Preco = 1.39D _ }) Adicionar(New Produto() With { _ .Id = 2, .Nome = "Table Ipad", .Categoria = "Informática", .Preco = 993.75D _ }) Adicionar(New Produto() With { _ .Id = 3, .Nome = "NoteBook CCE", .Categoria = "Informática", .Preco = 1016.99D _ }) End Sub Public Function Adicionar(item As Produto) As Produto Implements IProdutoRepositorio.Adicionar item.Id = _proximoId + 1 produtos.Add(item) Return item End Function Public Function Atualizar(item As Produto) As Boolean Implements IProdutoRepositorio.Atualizar Dim index As Integer = produtos.FindIndex(Function(p) p.Id = item.Id) If index = -1 Then Return False End If produtos.RemoveAt(index) produtos.Add(item) Return True End Function Public Function GetPorID(id As Integer) As Produto Implements IProdutoRepositorio.GetPorID Return produtos.Find(Function(p) p.Id = id) End Function Public Function GetTodos() As System.Collections.Generic.IEnumerable(Of Produto) Implements IProdutoRepositorio.GetTodos Return produtos End Function Public Sub Remover(id As Integer) Implements IProdutoRepositorio.Remover produtos.RemoveAll(Function(p) p.Id = id) End Sub End Class
O repositório mantém a lista na memória local. Por ser mais simples, fizemos assim neste exemplo; mas em um aplicativo real você iria armazenar os dados externamente – seja em um banco de dados ou no armazenamento em nuvem. Com o repositório padrão, será mais fácil alterar a implementação mais tarde.
Definindo um novo Controller
Como já definimos o nosso modelo, vamos agora definir um novo controlador para nossa aplicação. Um controlador é um objeto que lida com requisições HTTP. O assistente para novo projeto cria dois controladores para você quando criamos o projeto. Para vê-los, expanda a pasta Controllers no Solution Explorer. São eles:
- HomeController é um controlador ASP.NET MVC tradicional. É responsável por servir páginas HTML para o site e não é diretamente relacionada à Web API;
- ValuesController é um exemplo de controlador WebAPI.
Para criar um novo controlador, clique com o botão direito sobre a pasta Controllers e selecione Add e a seguir Controller. Na janela Add Controller, informe o nome ProdutosController e selecione o template Empty API controller e clique no botão Add.
Obs: cuidado para não selecionar Empty MVC Controller!
Um arquivo ProdutosController.vb será criado na pasta Controllers. Digite o código abaixo neste arquivo:
Imports System.Net Imports System.Web.Http Imports System.Net.Http Public Class ProdutosController Inherits ApiController Shared ReadOnly repository As IProdutoRepositorio = New ProdutoRepositorio() End Class
O código define um campo que trata uma instância de IProdutoRepositorio.
Vamos agora implementar os métodos no controlador para os serviços que desejamos expor.
1. Método que obtém uma lista de produtos ( método HTTP GET , URI : /api/produtos )
Public Function GetAllProducts() As IEnumerable(Of Produto) Return repositorio.GetTodos() End Function
O nome do método começa com “Get”, então, por convenção, ele mapeia para solicitações GET. Além disso, o método não tem parâmetros, de modo que mapeia para um URI sem “id” no caminho.
2. Método que obtém um produto pelo seu Id ( método HTTP GET , URI : /api/produtos/id )
Public Function GetProduto(id As Integer) As Produto Dim item As Produto = repositorio.GetPorID(id) If item Is Nothing Then Throw New HttpResponseException(New HttpResponseMessage(HttpStatusCode.NotFound)) End If Return item End Function
Este método também nome começa com “Get”, mas o método tem um parâmetro chamado ID. Este parâmetro é mapeado para o segmento “id” no caminho URI. A ASP.NET Web API converte automaticamente o ID para o tipo de dados correto (int) para o parâmetro.
O método getProduto lança uma exceção do tipo HttpResponseException se o ID não for válido. Essa exceção será traduzida pelo Framework em erro 404 (não encontrado).
3. Método que obtém os produtos pela categoria informada ( método HTTP GET , URI : /api/produtos?categoria=categoria )
Public Function GetProdutosPorCategoria(categoria As String) As IEnumerable(Of Produto) Return repositorio.GetTodos().Where(Function(p) String.Equals(p.Categoria, categoria, StringComparison.OrdinalIgnoreCase)) End Function
4. Método que cria um novo produto
Para criar um novo produto, o cliente envia uma requisição HTTP POST, com o novo produto no corpo da mensagem da requisição.
Public Function PostProduto(item As Produto) As Produto item = repositorio.Adicionar(item) Return item End Function
Para lidar com solicitações POST, vamos definir um método cujo nome começa com “Post”. O método utiliza um parâmetro de tipo de produto. Por padrão, os parâmetros com tipos complexos são desserializados a partir do corpo da requisição. Portanto, esperamos que o cliente envie-nos uma representação serializada de um objeto produto, usando XML ou JSON para a serialização.
Esta implementação vai funcionar, mas ela não esta completa pois falta o seguinte:
- Código de resposta: Por padrão, a framework Web API define o código de status de resposta para 200 (OK). Mas de acordo com o protocolo HTTP/1.1, quando uma requisição POST resultada na criação de um recurso, o servidor deve responder com o status 201 (Criado).
- Location (Localização): Quando o servidor cria um recurso, ele deve incluir a URI do novo recurso no cabeçalho Location da resposta.
Então, vamos usar os recursos da ASP.NET Web API que torna fácil manipular a mensagem de resposta HTTP. A seguir, temos uma implementação mais completa para o método acima:
Public Function PostProduto(item As Produto) As HttpResponseMessage item = repositorio.Adicionar(item) Dim response = Request.CreateResponse(Of Produto)(HttpStatusCode.Created, item) Dim uri As String = Url.Link("DefaultApi", New With { _ .id = item.Id _ }) response.Headers.Location = New Uri(uri) Return response End Function
Observe que o tipo de retorno do método agora é HttpResponseMessage. Ao retornar um HttpResponseMessage em vez de um produto, nós podemos controlar os detalhes da mensagem de resposta HTTP, incluindo o código de status e o cabeçalho Location.
O método CreateResponse cria um HttpResponseMessage e grava automaticamente uma representação serializada do objeto produto no corpo da mensagem de resposta.
5. Método que atualiza um produto
Public Sub PutProduto(id As Integer, produto As Produto) produto.Id = id If Not repositorio.Atualizar(produto) Then Throw New HttpResponseException(New HttpResponseMessage(HttpStatusCode.NotFound)) End If End Su
O nome do método começa com “Put”, de forma que a Web API faz a correspondência para uma requisição PUT. O método tem dois parâmetros: a identificação do produto e o produto atualizado. O parâmetro id é retirado do caminho URI, e o parâmetro produto é desserializado a partir do corpo da requisição. Por padrão, o framework ASP.NET Web API toma os tipos de parâmetros simples da rota e os tipos complexos do corpo da solicitação.
6. Método que deleta um produto
Public Function DeletaProduto(id As Integer) As HttpResponseMessage repositorio.Remover(id) Return New HttpResponseMessage(HttpStatusCode.NoContent) End Function
De acordo com a especificação HTTP, o método DELETE deve ser idempotente, o que significa que as diversas requisições DELETE para a mesma URI tem que ter o mesmo efeito que uma simples requisição DELETE. Portanto, o método não deve retornar um código de erro se o produto já foi deletado.
Se uma solicitação DELETE for bem-sucedida, ela pode retornar o status 200(OK), com uma entidade de corpo que descreve o estado, o status 202(Aceito), se a exclusão ainda está pendente ou o status 204(No Content) sem corpo da entidade. Neste exemplo, o método retorna o status 204.
Dessa forma o código completo do nosso controlador ficou assim:
Imports System.Net Imports System.Web.Http Imports System.Net.Http Public Class ProdutosController Inherits ApiController Shared ReadOnly repositorio As IProdutoRepositorio = New ProdutoRepositorio() Public Function GetAllProducts() As IEnumerable(Of Produto) Return repositorio.GetTodos() End Function Public Function GetProduto(id As Integer) As Produto Dim item As Produto = repositorio.GetPorID(id) If item Is Nothing Then Throw New HttpResponseException(New HttpResponseMessage(HttpStatusCode.NotFound)) End If Return item End Function Public Function GetProdutosPorCategoria(categoria As String) As IEnumerable(Of Produto) Return repositorio.GetTodos().Where(Function(p) String.Equals(p.Categoria, categoria, StringComparison.OrdinalIgnoreCase)) End Function Public Function PostProduto(item As Produto) As HttpResponseMessage item = repositorio.Adicionar(item) Dim response = Request.CreateResponse(Of Produto)(HttpStatusCode.Created, item) Dim uri As String = Url.Link("DefaultApi", New With { _ .id = item.Id _ }) response.Headers.Location = New Uri(uri) Return response End Function Public Sub PutProduto(id As Integer, produto As Produto) produto.Id = id If Not repositorio.Atualizar(produto) Then Throw New HttpResponseException(New HttpResponseMessage(HttpStatusCode.NotFound)) End If End Sub Public Function DeletaProduto(id As Integer) As HttpResponseMessage repositorio.Remover(id) Return New HttpResponseMessage(HttpStatusCode.NoContent) End Function End Class
Assim, demonstramos a criação de uma nova WEB API que expõe os serviços para realizar as operações CRUD.
Na continuação deste artigo vamos implementar a interface (a view), para que possamos usar os recursos implementados de maneira amigável.
- Faça o download do projeto completo ProdutosRepositorio.zip (sem a pasta packages: você deverá criar o projeto e substituir os arquivos principais)