Swagger - Cómo documentar servicios Web API de ASP.NET Core

Swagger es una herramienta basada en el estándar OpenAPI que nos permite documentar y probar nuestros Web APIs, para que sean fácilmente accesibles y entendibles por los usuarios o desarrolladores que pretendan utilizarlos.

Como complemento al artículo JSON Web Token - Seguridad en servicios Web API de .NET Core, en este Post veremos cómo integrar Swager en un proyecto Web API RESTful de .NET Core ya existente, así como habilitar la autenticación JWT en la interfaz de usuario (Swagger UI) para realizar las pruebas pertinentes a nuestros Web APIs.

swagger

 

Integrando Swagger en nuestro proyecto Web API de .NET Core

En primer lugar, abriremos el proyecto Web API RESTful que ya creamos anteriormente en al artículo JSON Web Token - Seguridad en servicios Web API de .NET Core.

Desde la consola de administración de paquetes NuGet (Herramientas > Administrador de paquetes NuGet > Consola del Administrador de paquetes), instalaremos la herramienta NSwag para ASP.NET Core.

PM> Install-Package NSwag.AspNetCore

El paquete NuGet NSwag, integrará la interfaz gráfica de Swager en nuestro proyecto, y además nos permitirá generar documentación y especificaciones OpenAPI para los Web APIs definidos en nuestra aplicación.

Configurando Swagger 

Una vez instalado el paquete NuGet, pasaremos a configurar Swagger como servicio en el método ConfigureServices(IServiceCollection services) de la clase Startup.cs:

        // This method gets called by the runtime. 
        // Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // CONFIGURACIÓN DEL SERVICIO DE AUTENTICACIÓN JWT
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options => 
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = Configuration["JWT:Issuer"],
                        ValidAudience = Configuration["JWT:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(
                            Encoding.UTF8.GetBytes(Configuration["JWT:ClaveSecreta"])
                        )
                    };
                });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("ApplicationDbContext")));

            // REGISTRAMOS SWAGGER COMO SERVICIO
            services.AddOpenApiDocument(document =>
            {
                document.Title = "Título del Web API";
                document.Description = "Descripción del Web API.";

                // CONFIGURAMOS LA SEGURIDAD JWT PARA SWAGGER,
                // PERMITE AÑADIR EL TOKEN JWT A LA CABECERA.
                document.AddSecurity("JWT", Enumerable.Empty<string>(), 
                    new OpenApiSecurityScheme
                    {
                        Type = OpenApiSecuritySchemeType.ApiKey,
                        Name = "Authorization",
                        In = OpenApiSecurityApiKeyLocation.Header,
                        Description = "Copia y pega el Token en el campo 'Value:' así: Bearer {Token JWT}."
                    }
                );

                document.OperationProcessors.Add(
                    new AspNetCoreOperationSecurityScopeProcessor("JWT"));
            });
        }

Como vemos en el código, además de añadir el servicio Swagger mediante el método extensor services.AddOpenApiDocument(...), le hemos habilitado la opción de seguridad document.AddSecurity(...), para que podamos incluir en la cabecera de las peticiones,  un Token JWT de autenticación.

Para finalizar la configuración, ya solo quedaría añadir al Pipeline Configure(IApplicationBuilder app, IHostingEnvironment env) de la aplicación los Middleware necesarios para su funcionamiento:

        // This method gets called by the runtime. 
        // Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
                app.UseDeveloperExceptionPage();
            else
                app.UseHsts();

            app.UseHttpsRedirection();

            // AÑADIMOS EL MIDDLEWARE DE AUTENTICACIÓN
            // DE USUARIOS AL PIPELINE DE ASP.NET CORE
            app.UseAuthentication();            

            // AÑADIMOS EL MIDDLEWARE DE SWAGGER (NSwag)
            app.UseOpenApi();
            app.UseSwaggerUi3();

            app.UseMvc();
        }

Accediendo a la interfaz gráfica - Swagger UI

Una vez realizada la configuración, ya podremos acceder a la interfaz gráfica de Swagger desde la dirección /swagger. El resultado inicial (sin documentación) sería el siguiente:

swagger-ui

Como podemos ver, Swagger ha construido por nosotros una interfaz gráfica para pruebas con los dos Controladores de Web API que habíamos definido en nuestro proyecto, Login.cs y Pais.cs.

Además, se ha incluido un botón [ Authorize ] en la interfaz, que nos permitirá añadir un Token JWT a la cabecera de las peticiones HTTP que realicemos hacia los Controladores de Web API:

swagger-jwt

Importante: Para su correcto funcionamiento, el Token JWT debe estar precedido de la palabra Bearer ('B' mayúscula) separada por un espacio.

 

Documentando nuestros Web APIs de .NET Core

Llegados a este punto, y después de haber probado la funcionalidad de nuestros Web APIs desde la interfaz Swagger UI, ya nos habremos dado cuenta del potencial y la facilidad de uso que la herramienta Swagger nos ofrece a la hora de desplegar nuestros proyectos Web API de .NET Core.

swagger-get-byid

Aun así, todavía nos queda un aspecto crucial para cualquier API que desarrollemos, o sea, la documentación.

Swagger nos permite aplicar descripciones y comentarios a los Controladores y Acciones de nuestros Web API, ya sea en la misma interfaz de usuario o a través de links que hagan referencia a archivos de documentación externa.

Creando el archivo de documentación XML

Antes de comenzar a documentar nuestros Web API, debemos habilitar en nuestro proyecto Web API la opción de generar archivo de configuración XML. Esto lo haremos situándonos en el proyecto, hacemos click con botón derecho, y seleccionamos la opción Compilación:

swagger-doc-xm

Este archivo XML generado, será utilizado por Swagger para aplicar a la interfaz de usuario las descripciones y comentarios que hayamos definido en nuestro proyecto.

Comentando los Controladores de Web API

En primer lugar, debemos dar a los Controladores de Web API una descripción que identifique a primera vista su funcionalidad, y/o en su caso, indicar mediante un Link donde podemos descargar los archivos de documentación externa del Web API (manuales, tutoriales etc.)

Esto los haremos mediante el atributo [SwaggerTag( ... )] de la siguiente manera:

    [SwaggerTag("Pais", 
                Description = "Web API para mantenimiento de Países.", 
                DocumentationDescription = "Documentación externa",
                DocumentationUrl = "http://rafaelacosta.net/pais-doc.pdf")]
    [Route("api/[controller]")]
    [ApiController]
    public class PaisController : ControllerBase
    {
      ...
      ...
    }

Importante: Debemos de tener en cuenta que la propiedad name= (la primera en orden) del atributo SwaggerTag, debe ser el nombre del Controlador al cual estamos aplicando la descripción. En el ejemplo anterior name= sería "Pais", ya que estamos haciendo referencia al Controlador PaisController.

    [SwaggerTag("Login",
                Description = "Web API para autenticación de Usuarios.",
                DocumentationDescription = "Documentación externa",
                DocumentationUrl = "http://rafaelacosta.net/login-doc.pdf")]
    [Route("api/[controller]")]
    [ApiController]
    [AllowAnonymous]
    public class LoginController : ControllerBase
    {
       ...
       ...
    }

Volviendo a la interfaz de Swagger, el resultado sería el siguiente:

swagger-api-desc

Comentando las Acciones de los Controladores de Web API

A continuación, documentaremos las Acciones HTTP de nuestros Controladores de Web API.

Esto lo haremos mediante los comentarios de documentación XML que nos proporciona C#. Estos comentarios, se integran en nuestro código añadiendo tres barras diagonales /// al principio de las Acciones, y son utilizados por el compilador para generar el archivo de configuración XML que vimos anteriormente.

Un ejemplo para la Acción GET que obtiene un objeto (país) por su Id, sería el siguiente:

        // GET: api/Pais/5
        /// <summary>
        /// Obtiene un objeto por su Id.
        /// </summary>
        /// <remarks>
        /// Aquí una descripción mas larga si fuera necesario. Obtiene un objeto por su Id.
        /// </remarks>
        /// <param name="id">Id (GUID) del objeto.</param>
        /// <response code="401">Unauthorized. No se ha indicado o es incorrecto el Token JWT de acceso.</response>              
        /// <response code="200">OK. Devuelve el objeto solicitado.</response>        
        /// <response code="404">NotFound. No se ha encontrado el objeto solicitado.</response>        
        [HttpGet("{id}")]
        [Authorize]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<Pais>> GetPais(Guid id)
        {
            var pais = await _context.Pais.FindAsync(id);

            if (pais == null)
            {
                return NotFound();
            }

            return pais;
        }

Importante: Como vemos en los comentarios de documentación XML, hemos añadido la etiqueta <response code="...">...</response> que nos indica que tipos de respuesta HTTP nos puede devolver la Acción en un momento determinado. Para que los comentarios de estas respuestas aparezcan en la interfaz de Swagger, es obligatorio indicar mediante el atributo [ProducesResponseType(StatusCodes. ...)] los mismos tipos de respuesta que definimos en los comentarios XML.

Volviendo a la interfaz Swagger, el resultado sería el siguiente:

swagger-get

Por último, para una Acción del tipo POST que crea un nuevo objeto en la base de datos, este sería un ejemplo:

        // POST: api/Pais
        /// <summary>
        /// Crea un nuevo objeto en la BD.
        /// </summary>
        /// <remarks>
        /// Aquí una descripción mas larga si fuera necesario. Crea un nuevo objeto en la BD.
        /// </remarks>
        /// <param name="pais">Objeto a crear a la BD.</param>
        /// <response code="401">Unauthorized. No se ha indicado o es incorrecto el Token JWT de acceso.</response>              
        /// <response code="201">Created. Objeto correctamente creado en la BD.</response>        
        /// <response code="400">BadRequest. No se ha creado el objeto en la BD. Formato del objeto incorrecto.</response>
        [HttpPost]
        [Authorize]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        [ProducesResponseType(StatusCodes.Status201Created)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public async Task<ActionResult<Pais>> PostPais(Pais pais)
        {
            _context.Pais.Add(pais);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetPais", new { id = pais.Id }, pais);
        }

El resultado en la interfaz Swagger, quedaría así:

swagger-post

 

  Compartir


  Nuevo comentario

El campo Comentario es obligatorio.
El campo Nombre es obligatorio.

  Comentarios

Sergio Sergio

Otra cuestion es que los comentarios de las acciones y las descripciones de los parametros se cargan bien cuando se ejecuta la api desde visual studio, pero desaparecen cuando se publica en el IIS de produccion. Es como si el xml se quedase fuera de la compilacion. ¿Alguna idea de a que puede ser debido?
Sergio Sergio

Hola, dos preguntas:
- ¿como has establecido el campo id como "required"?
- ¿como has incluido los "example values"?

Gracias y enhorabuena por el artículo.
Beatriz Beatriz

Muchas gracias por tu artículo! Fácil de seguir y muy didactivo ;)
Rafael Acosta Administrador Rafael Acosta

@JUAN AGUILAR:

Gracias por tu comentario.


Compartir los artículos en redes sociales y hacer click de vez en cuando en los banners de publicidad, es fundamental para que este Blog siga ofreciendo publicaciones de calidad y en español.


JUAN AGUILAR JUAN AGUILAR

Hola, Rafael
muchas gracias, me fue de mucha utilidad tu articulo.
te felicito por compartir este conocimiento.
Rafael Acosta Administrador Rafael Acosta

@Javier:

Gracias por tu comentario.


Yo también te animo a compartir los artículos de este Blog entre la comunidad de desarrolladores de habla hispana en Alemania.


Como sabes, compartir los artículos en las redes sociales y hacer click de vez en cuando en los banners de publicidad, es fundamental para que este Blog siga ofreciendo publicaciones de calidad y en español.


Saludos.


Javier Javier

Qué buen artículo, muchas gracias, te animo a que sigas trabajando en tu blog. Un saludo!
Rafael Acosta Administrador Rafael Acosta

@ASP:

Gracias por tu comentario. Compartir los artículos en la redes sociales es fundamental para que este Blog siga ofreciendo publicaciones de calidad y en español.


ASP ASP

Excelente artículo.
Siga publicando artículos como este. Se necesitan personas que publiquen estos artículos en español.


Utilizamos cookies propias y de terceros para mejorar nuestros servicios y ofrecerle una mejor experiencia de navegación. Si continúa navegando consideramos que acepta su uso. Más información   Acepto