.NET

25 out, 2012

ASP.NET MVC 4 – Criando uma Web API com suporte a operações CRUD

Publicidade

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:

  1. ASP.NET MVC 4 – Apresentação;
  2. ASP.NET MVC 4 – Usando Web API;
  3. 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:

  1. – retorna a representação do recurso em uma URI especificada. O GET não deve ter efeitos colaterais sobre o servidor;
  2. 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;
  3. POST – cria um novo recurso. O servidor atribui a URI para o novo objeto e retorna esta URI como parte da mensagem de resposta.
  4. 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:

  1.  HomeController é um controlador ASP.NET MVC  tradicional. É responsável por servir páginas HTML para o site e não é diretamente relacionada à Web API;
  2.  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)