Tutorial de Swagger UI y publicación de documentación

Swagger UI es una herramienta de visualización de documentación de API basada en la especificación OpenAPI (anteriormente especificación Swagger), que convierte documentos de especificación de API en una interfaz de documentación de API interactiva.

A través de Swagger UI, desarrolladores y usuarios pueden:

  • Ver información detallada de la API
  • Probar las solicitudes de API directamente en la interfaz
  • Ver ejemplos de solicitudes y respuestas
  • Conocer las funciones y parámetros de los diferentes endpoints de API

Ventajas de Swagger UI

  1. Visualización: Proporciona una interfaz de documentación de API clara e intuitiva
  2. Interactividad: Admite pruebas de API en línea, sin necesidad de herramientas adicionales
  3. Actualización en tiempo real: Tras los cambios de código, la documentación se actualiza automáticamente
  4. Estandarización: Basado en la especificación OpenAPI, mantiene la consistencia
  5. Compatibilidad con múltiples lenguajes: Adecuado para varios lenguajes de programación y frameworks

Integración de Swagger UI

1. Integración en proyectos Spring Boot

Agregar dependencias

Enpom.xmlAgregue las siguientes dependencias:

Ejemplo

<!-- Springfox Swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>

<!-- Springfox Swagger UI -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>3.0.0</version>
</dependency>

O use SpringDoc OpenAPI:

Ejemplo

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.14</version>
</dependency>

Configurar Swagger

Ejemplo

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Documentación de API")
                .description("Descripción detallada de la API")
                .version("1.0.0")
                .contact(new Contact("Equipo de desarrollo", "https://example.com", "[email protected]"))
                .build();
    }
}

Para SpringDoc OpenAPI:

Ejemplo

import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.Contact;

@Configuration
public class SwaggerConfig {
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("Documentación de API")
                        .version("1.0.0")
                        .description("Descripción detallada de la API")
                        .contact(new Contact()
                                .name("Equipo de desarrollo")
                                .email("[email protected]")
                                .url("https://example.com")));
    }

    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("public-apis")
                .pathsToMatch("/api/**")
                .build();
    }
}

2. Integración en proyectos Node.js Express

Instalar dependencias

npm install swagger-jsdoc swagger-ui-express --save

Configurar Swagger

Ejemplo

const express = require('express');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const app = express();

// Configuración de Swagger
const swaggerOptions = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Documentación de API',
      version: '1.0.0',
      description: 'Descripción detallada de la API',
      contact: {
        name: 'Equipo de desarrollo',
        email: '[email protected]',
        url: 'https://example.com'
      }
    },
    servers: [
      {
        url: 'http://localhost:3000',
        description: 'Servidor de desarrollo'
      }
    ]
  },
  apis: ['./routes/*.js'] // Ruta del archivo de rutas de la API
};

const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));

// ... otras rutas y middlewares

app.listen(3000, () => {
  console.log('Servidor ejecutándose en http://localhost:3000');
  console.log('Documentación de API disponible en http://localhost:3000/api-docs');
});

3. Uso en proyectos Python FastAPI

FastAPI integra Swagger UI por defecto:

Ejemplo

from fastapi import FastAPI

app = FastAPI(
    title="Documentación de API",
    description="Descripción detallada de la API",
    version="1.0.0",
    contact={
        "name": "Equipo de desarrollo",
        "email": "[email protected]",
        "url": "https://example.com",
    },
)

@app.get("/")
async def root():
    return {"message": "Hello World"}

Swagger UI está disponible por defecto en la ruta /docs; puede consultar la documentación visitando http://localhost:8000/docs.

Consulte el contenido detallado en:Documentación interactiva de FastAPI API


Escribir documentación de API

1. Anotaciones de documentación de API en proyectos Spring Boot

Ejemplo

import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
@Api(tags = "Gestión de usuarios")
public class UserController {

    @ApiOperation(value = "Obtener lista de usuarios", notes = "Obtener toda la información de los usuarios de forma paginada")
    @ApiResponses({
        @ApiResponse(code = 200, message = "Éxito"),
        @ApiResponse(code = 400, message = "Error de parámetros de solicitud"),
        @ApiResponse(code = 500, message = "Error interno del servidor")
    })
    @GetMapping("/")
    public Page<User> getUsers(
            @ApiParam(value = "Número de página", required = true) @RequestParam int page,
            @ApiParam(value = "Registros por página", required = true) @RequestParam int size) {
        // Lógica de negocio
        return userService.getUsers(page, size);
    }

    @ApiOperation(value = "Obtener un solo usuario", notes = "Obtener información del usuario por ID")
    @GetMapping("/{id}")
    public User getUser(
            @ApiParam(value = "ID de usuario", required = true) @PathVariable Long id) {
        // Lógica de negocio
        return userService.getUser(id);
    }

    @ApiOperation(value = "Crear usuario", notes = "Crear un nuevo usuario")
    @PostMapping("/")
    public User createUser(
            @ApiParam(value = "Información del usuario", required = true) @RequestBody UserDTO userDTO) {
        // Lógica de negocio
        return userService.createUser(userDTO);
    }
}

Para SpringDoc OpenAPI:

Ejemplo

import io.swagger.v3.oas.annotations.*;
import io.swagger.v3.oas.annotations.media.*;
import io.swagger.v3.oas.annotations.responses.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
@Tag(name = "Gestión de usuarios", description = "API relacionada con usuarios")
public class UserController {

    @Operation(
        summary = "Obtener lista de usuarios",
        description = "Obtener toda la información de los usuarios de forma paginada"
    )
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "Éxito"),
        @ApiResponse(responseCode = "400", description = "Error de parámetros de solicitud"),
        @ApiResponse(responseCode = "500", description = "Error interno del servidor")
    })
    @GetMapping("/")
    public Page<User> getUsers(
            @Parameter(description = "Número de página", required = true) @RequestParam int page,
            @Parameter(description = "Registros por página", required = true) @RequestParam int size) {
        // Lógica de negocio
        return userService.getUsers(page, size);
    }
}

2. Comentarios de documentación de API en proyectos Node.js Express

Ejemplo

/**
 * @swagger
 * /api/users:
 *   get:
* summary: Obtener lista de usuarios
* description: Obtener toda la información de los usuarios de forma paginada
* tags: [Gestión de usuarios]
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *         required: true
* description: Número de página
 *       - in: query
 *         name: size
 *         schema:
 *           type: integer
 *         required: true
* description: Registros por página
 *     responses:
 *       200:
* description: Éxito
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/User'
 *                 total:
 *                   type: integer
 *       400:
* description: Error de parámetros de solicitud
 *       500:
* description: Error interno del servidor
 */

router.get('/users', (req, res) => {
  // Lógica de negocio
});

/**
 * @swagger
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       required:
 *         - name
 *         - email
 *       properties:
 *         id:
 *           type: integer
* description: ID de usuario
 *         name:
 *           type: string
* description: Nombre de usuario
 *         email:
 *           type: string
 *           format: email
* description: Correo electrónico del usuario
 */

3. Documentación de API en proyectos Python FastAPI

Ejemplo

from fastapi import FastAPI, Query, Path
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI(title="API de gestión de usuarios")

class User(BaseModel):
    id: int
    name: str
    email: str
   
    class Config:
        schema_extra = {
            "example": {
                "id": 1,
                "name": "Zhang San",
                "email": "[email protected]"
            }
        }

@app.get(
    "/api/users/",
    summary="Obtener lista de usuarios",
    description="Obtener toda la información de los usuarios de forma paginada",
    response_model=List[User],
    tags=["Gestión de usuarios"]
)
async def get_users(
    page: int = Query(..., description="Número de página", ge=1),
    size: int = Query(..., description="Registros por página", ge=1, le=100)
):
    # Lógica de negocio
    return [
        {"id": 1, "name": "Zhang San", "email": "[email protected]"},
        {"id": 2, "name": "Li Si", "email": "[email protected]"}
    ]

@app.get(
    "/api/users/{user_id}",
    summary="Obtener un solo usuario",
    description="Obtener información del usuario por ID",
    response_model=User,
    tags=["Gestión de usuarios"]
)
async def get_user(
    user_id: int = Path(..., description="ID de usuario", ge=1)
):
    # Lógica de negocio
    return {"id": user_id, "name": "Zhang San", "email": "[email protected]"}

Personalizar Swagger UI

1. Configuración personalizada en Spring Boot

Ejemplo

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
            .paths(PathSelectors.regex("/api/.*"))
            .build()
            .apiInfo(apiInfo())
            .useDefaultResponseMessages(false)  // Deshabilitar mensaje de respuesta predeterminado
            .globalResponseMessage(RequestMethod.GET, globalResponses())  // Personalizar mensaje de respuesta global
            .securitySchemes(Arrays.asList(apiKey()))  // Configurar autenticación de seguridad
            .securityContexts(Arrays.asList(securityContext()));  // Configurar contexto de seguridad
}

private List<ResponseMessage> globalResponses() {
    return Arrays.asList(
            new ResponseMessageBuilder().code(200).message(Éxito).build(),
            new ResponseMessageBuilder().code(400).message(Error en los parámetros de la solicitud).build(),
            new ResponseMessageBuilder().code(401).message(No autorizado).build(),
            new ResponseMessageBuilder().code(403).message(Acceso prohibido).build(),
            new ResponseMessageBuilder().code(500).message(Error interno del servidor).build()
    );
}

private ApiKey apiKey() {
    return new ApiKey("JWT", "Authorization", "header");
}

private SecurityContext securityContext() {
    return SecurityContext.builder()
            .securityReferences(defaultAuth())
            .forPaths(PathSelectors.regex("/api/.*"))
            .build();
}

private List<SecurityReference> defaultAuth() {
    AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
    AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
    authorizationScopes[0] = authorizationScope;
    return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
}

2. Configuración personalizada en Express

Ejemplo

const options = {
  customCss: '.swagger-ui .topbar { display: none }',  // CSS personalizado
  customSiteTitle: Centro de documentación de API,  // Título de la página
  customfavIcon: "/favicon.png",  // Icono personalizado
  swaggerOptions: {
    persistAuthorization: true,  // Conservar información de autorización
    docExpansion: 'none',  // Plegar todas las interfaces por defecto
    tagsSorter: 'alpha',   // Ordenar etiquetas alfabéticamente
    operationsSorter: 'alpha', // Ordenar operaciones alfabéticamente
    defaultModelsExpandDepth: -1, // Ocultar modelos
    filter: true,  // Habilitar filtrado
  }
};

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs, options));

3. Configuración personalizada en FastAPI

Ejemplo

from fastapi import FastAPI
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.staticfiles import StaticFiles

app = FastAPI(
    title=Documentación de la API,
    docs_url=None,  # Deshabilitar la interfaz de Swagger UI predeterminada
)

app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + - Documentación de API,
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
        swagger_favicon_url="/static/favicon.png",
        swagger_ui_parameters={
            "docExpansion": "none",
            "defaultModelsExpandDepth": -1,
            "filter": True,
        }
    )

Publicación de documentación

1. Publicación interna

Para uso interno del equipo, se puede acceder directamente a través de la interfaz Swagger UI dentro de la aplicación:

  • Spring Boot: http://your-app-host:port/swagger-ui/index.html
  • Express: http://your-app-host:port/api-docs
  • FastAPI: http://your-app-host:port/docs

2. Generación de documentación estática

Usar Swagger Codegen para generar documentación estática

# 安装 Swagger Codegen CLI
wget https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.35/swagger-codegen-cli-3.0.35.jar -O swagger-codegen-cli.jar

# 生成静态 HTML 文档
java -jar swagger-codegen-cli.jar generate -i http://your-app-host:port/v3/api-docs -l html2 -o ./api-docs

Usar Redoc para generar documentación estática

# 安装 redoc-cli
npm install -g redoc-cli

# 生成静态 HTML 文档
redoc-cli bundle http://your-app-host:port/v3/api-docs -o ./api-docs/index.html

3. Integración en el flujo de CI/CD

Añadir pasos de generación y publicación de documentación en el pipeline de CI/CD:

Ejemplo de pipeline de Jenkins

Ejemplo

pipeline {
    agent any
   
    stages {
        // ... otras fases de compilación y prueba
       
        stage('Generate API Documentation') {
            steps {
                sh 'java -jar swagger-codegen-cli.jar generate -i http://your-app-host:port/v3/api-docs -l html2 -o ./api-docs'
            }
        }
       
        stage('Publish Documentation') {
            steps {
                // Publicar en el servidor de archivos estáticos Nginx
                sh 'rsync -avz --delete ./api-docs/ user@doc-server:/var/www/api-docs/'
               
                // O publicar en almacenamiento de objetos (como AWS S3)
                sh 'aws s3 sync ./api-docs/ s3://your-bucket/api-docs/ --delete'
            }
        }
    }
}

Ejemplo de flujo de trabajo de GitHub Actions

Ejemplo

name: Generate and Deploy API Docs

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
   
    steps:
    - uses: actions/checkout@v2
   
    - name: Set up JDK
      uses: actions/setup-java@v2
      with:
        distribution: 'adopt'
        java-version: '11'
   
    - name: Build application
      run: ./mvnw clean package
   
    - name: Start application
      run: |
        java -jar target/your-app.jar &
        sleep 30# Esperar a que la aplicación se inicie
   
    - name: Generate API Documentation
      run: |
        wget -q https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.35/swagger-codegen-cli-3.0.35.jar -O swagger-codegen-cli.jar
        java -jar swagger-codegen-cli.jar generate -i http://localhost:8080/v3/api-docs -l html2 -o ./api-docs
   
    - name: Deploy to GitHub Pages
      uses: JamesIves/github-pages-deploy-action@4.1.5
      with:
        branch: gh-pages
        folder: api-docs

4. Uso de plataformas de gestión de documentación de API

Además de implementar por su cuenta, también puede utilizar plataformas profesionales de gestión de documentación de API:

  • Swagger Hub: Proporciona servicios de diseño de API y alojamiento de documentación
  • Postman: No solo permite probar API, sino también publicar documentación de API
  • ReadMe.io: Proporciona gestión integral de documentación de API y portal para desarrolladores
  • Stoplight: Proporciona herramientas de diseño, documentación y gobernanza de API

5. Despliegue de Swagger UI con Docker

Ejemplo

FROM swaggerapi/swagger-ui:latest

# Establecer variables de entorno
ENV SWAGGER_JSON=/swagger/openapi.json
ENV BASE_URL=/api-docs

# Copiar el archivo de especificación OpenAPI
COPY openapi.json /swagger/

# Exponer el puerto
EXPOSE 8080

# Iniciar Swagger UI
CMD ["sh", "/usr/share/nginx/docker-run.sh"]

Construir y ejecutar:

docker build -t my-swagger-ui .
docker run -p 8080:8080 my-swagger-ui

Acceder: http://localhost:8080/api-docs


Mejores prácticas

1. Estándares de documentación

  • Mantener la claridad y la concisión: La descripción debe ser concisa y específica
  • Gestión por grupos: Utilizar etiquetas para agrupar lógicamente las API
  • Proporcionar ejemplos: Proporcionar ejemplos para solicitudes y respuestas
  • Manejo de errores estandarizado: Unificar el formato de respuesta de error y los códigos de estado
  • Control de versiones: Indicar claramente la información de versión de la API en la documentación

2. Configuración de seguridad

  • Manejo de información sensible: No exponer información sensible en la documentación
  • Configuración de entorno de producción: En el entorno de producción se puede deshabilitar o restringir opcionalmente el acceso a la documentación
  • Mecanismo de autenticación: Configurar la autenticación para el acceso a la documentación

Ejemplo

// Configurar Spring Boot para deshabilitar Swagger en producción
@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .enable(!environment.acceptsProfiles(Profiles.of("prod")))
            // ... otra configuración
}

3. Pruebas automatizadas

Combine documentación y pruebas para garantizar que el contenido de la documentación coincida con el comportamiento real de la API:

Ejemplo

// Spring Boot usando REST Assured y Swagger para pruebas
@Test
public void validateSwaggerDocumentation() {
    // Obtener el JSON de Swagger
    String swaggerJson = given()
            .when()
            .get("/v3/api-docs")
            .then()
            .statusCode(200)
            .extract().asString();
   
    // Verificar la documentación de Swagger
    OpenAPI openAPI = new OpenAPIParser().readContents(swaggerJson, null, null).getOpenAPI();
    assertNotNull(openAPI);
   
    // Verificar si un endpoint específico está en la documentación
    assertNotNull(openAPI.getPaths().get("/api/users"));
}

Solución de problemas comunes

1. Swagger UI no se muestra o falla al cargar

  • Verificar la versión de las dependencias: Asegurarse de que las versiones de las dependencias sean compatibles
  • Verificar la clase de configuración: Asegurarse de que la clase de configuración esté correctamente registrada
  • Verificar el mapeo de rutas: Asegurarse de que el mapeo de rutas sea correcto
  • Verificar la configuración de CORS: Si hay acceso entre dominios, asegurarse de que la configuración de CORS sea correcta

2. Información de interfaz incompleta

  • Verificar las anotaciones: Asegurarse de que todas las anotaciones necesarias se hayan añadido
  • Verificar la ruta de escaneo de paquetes: Asegurarse de que la ruta de escaneo incluya todos los controladores
  • Verificar las clases de modelo: Asegurarse de que las clases de modelo tengan la descripción correcta

3. Problemas de seguridad en el entorno de producción

Configurar el entorno de producción para deshabilitar o proteger la documentación:

# application-prod.properties
springdoc.swagger-ui.enabled=false
springdoc.api-docs.enabled=false

O proteger con autenticación básica:

Ejemplo

@Configuration
@Profile("prod")
public class SwaggerSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
            .antMatchers("/swagger-ui/**", "/v3/api-docs/**")
            .and()
            .authorizeRequests()
            .anyRequest().hasRole("ADMIN")
            .and()
            .httpBasic();
    }
}
```

Resumen

Swagger UI es una potente herramienta de documentación de API. Con una configuración y uso correctos, puede mejorar en gran medida la usabilidad de la API y la eficiencia del desarrollo. Este tutorial presenta los conceptos básicos de Swagger UI, métodos de integración, redacción de documentación, configuración personalizada, publicación de documentación y mejores prácticas, con la esperanza de que sea útil para su trabajo de documentación de API.

Recuerde los siguientes puntos clave:

  1. Elija el método de integración de Swagger adecuado para la pila tecnológica del proyecto
  2. Diseñe y escriba cuidadosamente las anotaciones o comentarios de la documentación de API
  3. Personalice la interfaz de Swagger UI según sea necesario
  4. Seleccione el método adecuado de publicación de documentación
  5. Siga las mejores prácticas para garantizar la precisión y seguridad de la documentación

Mediante un uso adecuado de Swagger UI, puede proporcionar a su API una documentación profesional e interactiva, facilitando la comprensión y el uso de su API.

Otras extensiones