API Rest simples com Asp.Net Core
Tags: braziliandevs, aspnetcore, restapi
Published: 2021-03-08 16:43:23

# API Rest simples para retornar dados

## 1. Criando controller

Para o controller, é criado uma classe que herda de `ControllerBase`. Existe também a herança de `Controller`, porém ela é utilizada somente em projetos com manipulação de páginas da Web, não para solicitações de API WEB.

A classe recebe a anotação `[ApiController]`, que fornece algumas [funcionalidades](https://docs.microsoft.com/pt-br/aspnet/core/web-api/?view=aspnetcore-5.0#apicontroller-attribute) como binding sources, validações com `[Required]`, fazendo com que seja disparado o erro 400 Bad Request automáticamente no caso de erro de validação. Além disso, [resultados de erro](https://docs.microsoft.com/pt-br/aspnet/core/web-api/?view=aspnetcore-5.0#problem-details-for-error-status-codes) (código de status 400 ou superior) irão retornar uma resposta em JSON que segue a RFC 7807. 
```json
{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}
```

Em seguida, a classe recebe uma anotação `[Route("api/[controller]")`. O argumento `[controller]` faz com que a rota receba o nome da classe, tirendo o sufixo Controller. Por exemplo, uma classe chamada Palavras**Controller** terá, nesse caso, a rota **api/Palavras**.

## 2. Criando uma rota GET

As rotas seguem uma estrutura de pasta.
```
api/Palavras
    GetDefinicao   
```
No caso de uma classe que está recebendo a rota **api/Palavras**. Caso seja criado um método, por exemplo, em uma rota customizada chamada **GetDefinicao**, a URI da rota será de **api/Palavras/GetDefinicao**.

O método da rota recebe uma anotação indicando sua operação HTTP (GET, POST, PUT, PATCH, DELETE ETC). No caso de uma rota para busca de recursos, utiliza-se `[HttpGet]`.

Uma rota GET de recursos geralmente busca os recursos utilizando um repositório, guardando o resultado em uma variável. Em seguida, é retornado os valores para o cliente, que serão automaticamente mapeados para JSON. 

### Retornos
No ASP.NET Core existe métodos para retornar status code, como `BadRequest()` e `NotFound()`. Esses métodos estão disponíveis ao definir o tipo de retorno da rota como `ActionResult` ou `IActionResult`. Nesse caso, utilizaremos o método para retornar Ok junto do conteúdo, que é passado como argumento. É interessante sempre retornar esses status codes, ao invés de lançar uma exceção para que o cliente receba a resposta em JSON e a aplicação não pare.

Para retornar um objeto ou lista de objetos, é passado o tipo dentro de `ActionResult`. No caso de uma lista de objetos da classe Command, é retornado .
```c# 
ActionResult<IEnumerable<Palavra>>
// ou
ActionResult<IList<Palavra>>
```

A diferença entre passar um tipo para `ActionResult<T>` ou utilizar `IActionResult` sem passar um tipo é a de que, no primeiro caso, será retornado automaticamente um `Ok()` se o tipo for o mesmo do específicado, pois o retorno é convertido para ``ObjectResult``. Caso contrário, será retornado um status code de erro


`IActionResult` é útil quando o retorno pode ser de diversos tipos.

Para rotas que utilizam repositórios async, é passado a assinatura async com o retorno Task, como `Task<IActionResult>`, lembrando de passar a assinatura **await** no método assíncrono. Métodos async são úteis quando se espera uma operação externa, como por exemplo esperar o banco retornar os dados requisitados. Dessa maneira, a aplicação não é bloqueada enquanto aguarda esse retorno, podendo fazer outras requisições com outros clientes. E.g 
```c# 
var results = await _repository.GetAllPalavras(includeTalks);
```
Código completo
```c#
[HttpGet]        
public async Task<IActionResult> Get()
{
    try
    {
        // Por ser async, é necessario await
        // Caso contrário, será retornado o OK mesmo sem atribuir o results.
        var palavras = await _repository.GetAllPalavras();
        var retorno = new
        {
            Count = results.Count(),
            Results = palavras
        };        
        return Ok(retorno);
    }
    catch (Exception)
    {        
        return this.StatusCode(StatusCodes.Status500InternalServerError, "Database failure");
    }
}
```

O código acima retorna os registros no banco para o cliente. Se acontecer algum erro, é considerado como uma falha do servidor, retornando o código 500. Isso pois esse método get não recebe argumentos, como por exemplo o Id de uma entidade, que retornaria o `NotFound()`. Por conta disso, caso ocorra algum erro, é por parte do servidor. Por não existir um método para Internal server error, é utilizado o `StatusCodes`.

Caso o retorno fosse ActionResult<Palavra>, bastaria retornar com a linha `return result;`, que seria convertido automaticamente para um `Ok()`.

O método acima retorna um objeto anônimo contendo a quantidade de registros no banco e a lista com os registros. Caso queiramos uma rota que apenas retorna a lista, utilizamos do `ActonResult<List<Palvra>>`

```c#
[HttpGet]        
public async Task<ActionResult<List<Palavra>>> Get()
{
    try
    {  
        var palavras = await _repository.GetAllPalavras();
        // Pelo retorno ser explícito, é retornado o Ok() automaticamente.
        // Caso o retorno não seja igual ao especificado, é retornado um erro.
        return retorno;
    }
    catch (Exception)
    {        
        return this.StatusCode(StatusCodes.Status500InternalServerError, "Database failure");
    }
}
```