Creando un API en Net Core - Mi primer API
Tags: csharp, api, dotnet, rest
Published: 2021-02-03 21:42:28

Llegó el tan ansiado momento de meter mano en el código. Durante este artículo vamos a crear nuestro primer **API** e implementar algunas de las cosas que vimos en los artículos anteriores.

> Pueden clonarse el proyecto desde [github](https://github.com/andreslozadamosto/MyFirstNetCoreAPI-Tutorial).

*** 
# Creando nuestro primer proyecto
Si estamos utilizando un _IDE_ como _Visual Studio_ o _Rider_ simplemente vamos a la opción de crear un nuevo proyecto, seleccionamos el tipo de proyecto **Web API** y seguimos las instrucciones. Con esta acción se generará un nuevo proyecto utilizando como base el template que viene integrado en **_Net Core_** y seleccionará la versión de **_Net Core_** más nueva que se encuentre instalada en el sistema (recomiendo utilizar la última, **_Net Core 5_**).

En cambio si estamos usando la línea de comandos y un editor como _VS Code_ para programar, debemos seguir estos pasos para crear la estructura del proyecto

```bash
// creamos la carpeta de la solución
$> mkdir MyFirstNetCoreWebAPI

// creamos el archivo de solución
$> dotnet new sln --name MyFirstNetCoreWebAPI

// creamos la carpeta de código
$> mkdir src
$> cd src

// creamos el proyecto de web api
$> dotnet new webapi --name MyFirstNetCoreWebAPI.WebAPI

// agregamos el proyecto a la solución
$> cd ..
$> dotnet sln .\MyFirstNetCoreWebAPI.sln add .\src\MyFirstNetCoreWebAPI.WebAPI\MyFirstNetCoreWebAPI.WebAPI.csproj
```

Si nos gusta trabajar con la consola y/o queremos utilizar un _IDE_ liviano como _VS Code_ talvez convenga instalar el generador de **dotnet** que nos ayudará a crear los elementos necesarios (mas allá de los [plugins](https://code.visualstudio.com/docs/languages/csharp) de _VS Code_).

```bash
$> dotnet tool install -g dotnet-aspnet-codegenerator

## Agregamos el paquete al proyecto
$> dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
```

Ya sea que creemos desde donde creemos el proyecto, luego éste puede utilizarse desde cualquier _IDE_.

Con el proyecto creado, borramos las 2 clases que se crean de forma automática: `WeatherForecast.cs` y `WeatherForecastController.cs`. Es código del template que se agrega únicamente como referencia pero que nosotros no vamos a utilizar.

La estructura base que les debe quedar es la siguiente:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oompm3sqsfgfls3pcpv7.jpg)

Para los que son nuevos en **_Net Core_** vamos a repasar qué es cada cosa:
- `Program.cs`: es el archivo que crea y levanta el Host donde se va a ejecutar el web api
- `Startup.cs`: es la clase de inicialización de la aplicación. En ella se realiza la configuración del servicio/app/web como por ejemplo la configuración de seguridad, de logging, de _OpenAPI_/_Swagger_, motor de inyección de dependencias (IOC), etc
- `appsettings.json`: es un archivo de configuración externo y variable por ambiente
- `Controllers`: esta carpeta estarán los controllers que son los puntos de entrada de las URIs/endpoints del **API**.

***
## Nuestro primer recurso
Para nuestro ejemplo vamos a utilizar el recurso `usuarios` en donde vamos a poder listar los usuarios existentes, consultar por un usuario en particular y agregar un nuevo usuario. 

Lo primero que debemos realizar es crear una clase controller que se llame `UsersController` ya sea con el _IDE_ o con el generador de código.

```bash
$> dotnet aspnet-codegenerator controller -api -async -name UsersController -outDir Controllers
```

Si ahora vemos el file system o el explorador del _IDE_ vemos que se creó un archivo con la estructura básica de un `Controller` de **Web API**.

Dentro del controller comenzamos a agregar la lógica necesaria.

**Listado de todos los usuarios**
```bash
[HttpGet()]
public ActionResult<List<UserDto>> GetAllUsers() 
	=> Ok(_userRepository
		.GetAll()
		.Select(x => UserDto.FromModel(x)).ToList());
```
**Obtener un usuario en particular**
```bash
[HttpGet("{name}")]
public ActionResult<UserDto> GetUser(string name)
{
	var user = _userRepository.Get(name);
	if (user == null)
		return Problem(
			"User not found",
			HttpContext.Request.Path,
			StatusCodes.Status404NotFound,
			"Bad parameters");

	return Ok(UserDto.FromModel(user));
}
```

**Crear un nuevo usuario**
```bash
[HttpPost()]
public ActionResult<UserDto> AddUser([FromBody] UserDto newUserToCreate)
{
	if (string.IsNullOrEmpty(newUserToCreate.Name))
		return BadRequest(new ProblemDetails()
		{
			Title = "Bad parameters",
			Detail = "The name of the new user cannot be empty or null",
			Instance = HttpContext.Request.Path
		});

	var userAlreadyExists = _userRepository.Exists(newUserToCreate.Name);
	if (userAlreadyExists)
		return Conflict(new ProblemDetails()
		{
			Detail = "User already exists",
			Title = "Bad parameters",
			Instance = HttpContext.Request.Path
		});

	var newUser = new User(newUserToCreate.Name);

	_userRepository.Add(newUser);

	return Created($"/users/{newUserToCreate.Name}", UserDto.FromModel(newUser));
}
```

Es un ejemplo simple y bastante trivial de una **API*, pero podemos ver varios de lo visto en los artículos anteriores:
- **URI de recurso**: el recurso _usuario_ tiene su URI de acceso http://domain.com/api/users. Esto lo provee el _Atributte_ `[Route("api/[controller]")]` que se encuentra al inicio de la clase. 
- **HTTP Verbs**: cada acción sobre el recurso está asociado al _verbo HTTP_ correcto. Los que son de consulta utilizamos _GET_ y el que es de creación _POST_.
- **Safe**: las acciones sobre el recurso utilizando el verbo _GET_ no genera efectos secundarios sobre el recurso
- **Idempotencia**: Los Get siempre devuelven la misma información ante múltiples peticiones iguales.
- **Códigos de respuesta**: Utilizamos los correctos para cada caso, _200_ para request satisfactorio devolviendo los datos solicitados, _201_ para cuando se crea un nuevo recurso, _404_ para cuando no es encontrado el recurso solicitado y _400_ si los datos que vinieron en el request tienen algún problema.
- **Open API**: el scaffold del template ya viene con la implementación básica de _OpenAPI_/_Swagger_ por lo que la documentación está disponible al ejecutar la aplicación.

Aquí podemos ver la clase completa
```csharp
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
	private readonly IUserRepository _userRepository;
	
	public UsersController(IUserRepository userRepository)
	{
		_userRepository = userRepository;
	}

	[HttpGet()]
	public ActionResult<List<UserDto>> GetAllUsers() 
		=> Ok(_userRepository
			.GetAll()
			.Select(x => UserDto.FromModel(x)).ToList());

	[HttpGet("{name}")]
	public ActionResult<UserDto> GetUser(string name)
	{
		var user = _userRepository.Get(name);
		if (user == null)
			return Problem(
				"User not found",
				HttpContext.Request.Path,
				StatusCodes.Status404NotFound,
				"Bad parameters");

		return Ok(UserDto.FromModel(user));
	}

	[HttpPost()]
	public ActionResult<UserDto> AddUser([FromBody] UserDto newUserToCreate)
	{
		if (string.IsNullOrEmpty(newUserToCreate.Name))
			return BadRequest(new ProblemDetails()
			{
				Title = "Bad parameters",
				Detail = "The name of the new user cannot be empty or null",
				Instance = HttpContext.Request.Path
			});

		var userAlreadyExists = _userRepository.Exists(newUserToCreate.Name);
		if (userAlreadyExists)
			return Conflict(new ProblemDetails()
			{
				Detail = "User already exists",
				Title = "Bad parameters",
				Instance = HttpContext.Request.Path
			});

		var newUser = new User(newUserToCreate.Name);

		_userRepository.Add(newUser);

		return Created($"/users/{newUserToCreate.Name}", UserDto.FromModel(newUser));
	}
}
```

## Problem Details
En el API otra cosa que podemos ver es la utilización del estándar de [ProblemDetails`](https://tools.ietf.org/html/rfc7807#:~:text=RFC%207807%20Problem%20Details%20March%202016%20Finally%2C%20an%20application%20might,replace%20existing%20domain%2Dspecific%20formats) para notificar el error producido en la ejecución del request.

Esta manera de comunicar que fue lo sucedido en el **API** estamos convalidando el principio de _Interfaz uniforme_ que mencionamos anteriormente. 

Lo que define este estándar es como un **HTTP API** debe informar lo sucedido en la aplicación cuando se devuelve un código de error _3xx_, _4xx_ o _5xx_.

Por suerte para nosotros **_Net Core_** resuelve este tema de manera automática para las excepciones no controladas, parámetros vacíos y demás controles que realiza internamente antes de ejecutar nuestro código del controller, quedando de nuestro lado implementarlo en nuestros métodos.

En el ejemplo del `Controller`, vemos que hay 2 maneras de implementarlo, una es llamando al método `Problems` y el 2do es llamado al método del **HTTP Status** que queremos retornar y pasándole como parámetro una instancia de la clase `ProblemDetails`. Ambos métodos son igualmente válidos.

Del código también se desprende que no devolvemos directamente el objeto User sino que lo mapeamos a una clase `UserDto`. Esto es así porque por lo general obtendremos el usuario de una base de datos por medio de Entidades de Negocio. Nunca es una buena práctica  devolver una _Entidad de Negocio_ al _Frontend_, sino devolver un objeto que defina un contrato entre el **API** y el cliente que realiza el request sin que esté acoplado al negocio.

***

Felicitaciones!!! 🎉🎉

Ya tenemos nuestro primer **API** desarrollado. En las próximas entregas iremos agregando poder a nuestro **API**.

No se olviden de bajar el código de [Github](https://github.com/andreslozadamosto/MyFirstNetCoreAPI-Tutorial) y de dejar en los comentarios si quieren que veamos algo en particular en los próximos artículos de la serie.