1. Introducción a Swagger
En el artículo anterior, presentamos el soporte de Spring Boot para RESTFUL. En este artículo, seguimos discutiendo este tema. Sin embargo, ya no discutiremos cómo se implementa la API RESTful, sino que discutiremos el mantenimiento de la documentación de API RESTful.
En el trabajo diario, a menudo necesitamos proporcionar interfaces al front-end (Fin web, iOS, Android) o terceros. En este momento, necesitamos proporcionarles una documentación de API detallada. Pero mantener un documento detallado no es una tarea fácil. En primer lugar, escribir un documento detallado es una tarea laboriosa y que requiere mucho tiempo. Por otro lado, dado que el código y el documento están separados, es fácil causar inconsistencias entre el documento y el código. En este artículo, compartiremos una forma de mantener documentos API, es decir, generar automáticamente documentos de API reestuosos a través de Swagger.
Entonces, ¿qué es Swagger? Podemos leer directamente la descripción oficial:
El API Toolingswagger más popular del mundo es el marco más grande del mundo de las herramientas de desarrollador de API para la especificación OpenAPI (OAS), permite el desarrollo en todo el ciclo de vida de la API, desde el diseño y la documentación, hasta la prueba y la implementación.
Este pasaje primero le dice que Swagger es la herramienta API más popular del mundo, y el propósito de Swagger es apoyar el desarrollo de todo el ciclo de vida de la API, que incluye diseño, documentación, pruebas e implementación. En este artículo, utilizaremos las funciones de gestión de documentos y pruebas de Swagger.
Después de obtener una comprensión básica del papel de Swagger, echemos un vistazo a cómo usarlo.
2. Integración de botas de arrogancia y primavera
Paso 1: Introducir el paquete JAR correspondiente:
<Spendency> <MoupRoMID> io.springfox </groupid> <artifactid> springfox-swagger2 </arfactid> <verserse> 2.6.0 </versión> </pendency> <epardency> <proupID> io.springfox </proupid> <artifactid> springfox-swagger-ui </artifactid> <version> 2.6.0 </Versión </versional
Paso 2: Configuración de información básica:
@Configuration @habilswagger2Public Class swagger2Config {@Bean Public Docket creeaterestapi () {return New Docket (DocumationType.Swagger_2) .ApiInfo (apiinfo ()) .select () .apis (requestlerselectores.basePackage ("com.pandy.blog.rest")))))) .paths (PathSelectors.regex ("/REST /.*")) .Build (); } private apiinfo apiInfo () {return new apiInfobuilder () .title ("Blog System Restful API") .Description ("Blog System Restful API") .TERMSOFServiceUrl ("http://127.0.0.1:8080/") .contact ("liuxiaopeng"). Versión ("" "). .construir(); }}La configuración básica es una descripción de todo el documento API y algunas configuraciones globales, que funcionan para todas las interfaces. Hay dos anotaciones involucradas aquí:
@Configuration significa que esta es una clase de configuración, anotación proporcionada por JDK y se ha explicado en el artículo anterior.
@La función de habilitadowagger2 es habilitar funciones relacionadas con Swagger2.
En esta clase de configuración, instancié un objeto de expediente, que incluye principalmente tres aspectos de la información:
(1) La información de descripción de toda la API, es decir, la información incluida en el objeto APIINFO, esta parte de la información se mostrará en la página.
(2) Especifique el nombre del paquete para generar el documento API.
(3) Especifique la ruta para generar la API. La API basada en la ruta puede admitir cuatro modos, que se pueden usar para consultar su código fuente:
Public Class PathSelectors {private PathSelectors () {Throw New UnpportedOperationException (); } Predatado estático público <String> Any () {return predicates.alwaystrue (); } Predatado estático público <String> None () {return predicates.alwaysfalse (); } public Predication estático <String> Regex (String final PathReGex) {return New predication <String> () {public boolean Aplicat (String Input) {return input.matches (PathREGEX); }}; } public Predicate estático <String> Ant (Final String Antpattern) {return New predication <String> () {public boolean Aplicat (String Input) {antpathMatcher matcher = new AntPathMatcher (); return matcher.match (antpatrón, entrada); }}; }}Como se puede ver en el código fuente, Swagger admite cuatro maneras: generación de cualquier ruta, no generación de ninguna ruta y coincidencia regular y coincidencia de patrones de hormigas. Puede estar más familiarizado con los primeros tres tipos, el último de la coincidencia de hormigas. Si no está familiarizado con Ant, simplemente ignórelo. Los primeros tres tipos deberían ser suficientes para que todos los usen en el trabajo diario.
Con la configuración anterior, podemos ver el efecto. Tengo una clase de ArticlerestController bajo el paquete com.pandy.blog.rest. El código fuente es el siguiente:
Comience el arranque de la primavera y luego visite: http://127.0.0.1:8080/swagger-ui.html para ver los siguientes resultados:
Puede ver en esta página que, excepto la última interfaz /test /{id}, las otras interfaces generan documentos correspondientes. Debido a que la última interfaz no cumple con la ruta que configuramos: "/Rest/.*", no se genera ningún documento.
También podemos hacer clic para ver cada interfaz específica. Tomemos la interfaz "Post /REST /Artículo" como ejemplo:
Como puede ver, Swagger genera ejemplos de resultados de retorno y parámetros de solicitud para cada interfaz, y puede acceder directamente a la interfaz a través de la "Prueba" a continuación. En términos de la interfaz, todos prueban la interfaz. En general, Swagger sigue siendo muy potente y la configuración es relativamente simple.
@RestControllerPublic Class ArticlerestController {@aUtowired ArtículoService ArticlesService; @RequestMapping (valor = "/REST/Artículo", método = post, produce = "aplicación/json") public webResponse <map <string, object >> saveartle (@RequestBody Artículo Artículo) {Artículo.setUserID (1l); ArticleService.Saveartle (artículo); Map <string, object> ret = new HashMap <> (); ret.put ("id", artículo.getId ()); WebResponse <map <string, object >> respuesta = webResponse.getSuccessResponse (ret); Respuesta de retorno; } @RequestMapping (valor = "/REST/Artículo/{id}", método = delete, produce = "Application/JSON") public WebResponse <?> Deleteartle (@PathVariable Long ID) {Artículo Artículo = ArticlesService.getById (id); Artículo.setStatus (-1); ArticleService.updateartle (artículo); WebResponse <S Object> Response = WebResponse.GetSuccessResponse (NULL); Respuesta de retorno; } @RequestMapping (value = "/REST/Artículo/{id}", método = put, produce = "aplicación/json") public webResponse <S Object> UpdateArtle (@PathVariable Long ID, @RequestBody Artículo Artículo) {Artículo.setId (id); ArticleService.updateartle (artículo); WebResponse <S Object> Response = WebResponse.GetSuccessResponse (NULL); Respuesta de retorno; } @RequestMapping (value = "/REST/Artículo/{id}", método = get, produce = "aplicación/json") public webResponse <artic> getAtticle (@PathVariable Long Id) {Artículo Artículo = ArticleService.getById (id); WebResponse <ic artículo> respuesta = webResponse.getSuccessResponse (artículo); Respuesta de retorno; } @RequestMapping (valor = "/test/{id}", método = get, produce = "aplicación/json") public webResponse <?> getNoapi () {webResponse <?> Respuesta = webResponse.getSuccessResponse (null); Respuesta de retorno; }}3. Configuración detallada de la API de Swagger
Pero definitivamente tendrás algunas preguntas cuando veas esto:
La primera pregunta: no hay una descripción literal del resultado de retorno y los parámetros de solicitud. ¿Se puede configurar esto?
La segunda pregunta: este parámetro de solicitud debe reflejarse directamente en función del objeto, pero no se requieren todas las propiedades del objeto, y el valor del parámetro puede no satisfacer nuestras necesidades. ¿Se puede configurar esto?
La respuesta ciertamente está bien. Ahora resuelvamos estos dos problemas y veamos directamente el código de configuración:
paquete com.pandy.blog.rest; import com.pandy.blog.dto.webresponse; import com.pandy.blog.po.article; import io.swagger.annotations.apioperation; import io.swagger.annotations.apiresponse; import io.swagger.annotations.apiresponse; import io.swagger.annotations.apiresponse; import io.swagger.annotations.apiresponse; import org.springframework.context.annotation.profile; import org.springframework.web.bind.annotation.pathvariable; import org.springframework.web.bind.annotation.requestbody; import og.springfframework.web.bind.annotation.requestmapping; importación; importación; org.springframework.web.bind.annotation.restController; import java.util.hashmap; import java.util.list; import java.util.map; importación estática org.springframework.web.bind.annotation.requestmethod.get; import static org.springframework.web.bind.annotation.requestmethod.post; import static org.springframework.web.bind.annotation.requestmethod.put;@restsroller@requestmapping ("/depil") ArticlerestController {@aUtowired Private ArtlesService Artículos service; @RequestMapping (valor = "/artículo", método = post, produce = "aplicación/json") @apioperation (value = "agregar artículo", notas = "agregar un nuevo artículo", etiqueta = "artículo", httpmethod = "post") @ApiimpliceTparam (name = "sumario", valor = "artículo resumido", requerido = true, datatype = "string"), @apiimpliceTparam (name = "status", value = "status", requerir = true, dataType = "entero")}) @apiresponses ({@apiresponse (código = 200, mensaje = "exitoso", respuesta ", weer") @apiresponses) public WebResponse <map <string, object >> saveartle (@RequestBody Artículo Artículo) {ArticlesService.Saveartle (artículo); Map <string, object> ret = new HashMap <> (); ret.put ("id", artículo.getId ()); WebResponse <map <string, object >> respuesta = webResponse.getSuccessResponse (ret); Respuesta de retorno; } @Apioperation (value = "Eliminar artículo", notas = "Eliminar artículo por id", etiqueta = "artículo", httpmethod = "eliminar") @apiimpliceitParams ({@apiIplicitParam (name = "id", value = "ID de artículo", requerido = verdadero, datatype = "long")}) @RequestMapping (value = "/"/} ", requerir Delete, produce = "aplicación/json") public WebResponse <?> Deleteartle (@PathVariable Long Id) {Artículo Artículo = ArticlesService.getById (id); Artículo.setStatus (-1); ArticleService.Saveartle (artículo); return webResponse.getSuccessResponse (new HashMap <> ()); } @Apioperation (value = "Obtener la lista de artículos", notas = "consulta completa según el título", etiqueta = "artículo", httpmethod = "get") @apiimpliceTparams ({@apiimpliceitParam (name = "title", value = "título de artículo", requerido = falso, datatype = "cadena"), @ApiMapiParam (nombre = "Título", Título ", Valor", requerido ". "Número de artículos por página", requerido = false, dataType = "entero"), @apiimpliceitParam (name = "PageNum", value = "PagePage Number", requerido = false, dataType = "Integer")}) @RequestMapping (valor = "/artículo/list", método = get, productes = "Aplicación/json") Public WeBRessesw <? PageSize, Integer Pagenum) {if (pageSize == null) {pageSize = 10; } if (pagenum == null) {pagenum = 1; } int offset = (Pagenum - 1) * PageSize; Lista <artículo> Artículos = ArticlesService.GetArticles (título, 1L, Offset, PageSize); return webResponse.getSuccessResponse (artículos); } @Apioperation (value = "update artículo", notas = "actualizar el contenido del artículo", etiqueta = "artículo", httpmethod = "put") @apiimpliceitParams ({@apiimpliceTParam (name = "id", value = "ID de artículo", requería = verdadero, dataType = "Long"), @APIiMpplicParam (name = "Título", Título ", requirió el título", requirió el título de datos = ". datatype = "string"), @apiimpliceTParam (name = "summary", value = "Artículo sumario", requerido = false, datatype = "string"), @apiimpliceTparam (name = "status", valor = "publicar status", requerir = false, datatype = "entero")}) @RequestMapping (value = "/artículo "Aplicación/JSON") public WebResponse <?> UpdateArtles (@PathVariable Long ID,@SoldBody Artículo Artículo) {Artículo.setId (id); ArticleService.updateartle (artículo); return webResponse.getSuccessResponse (new HashMap <> ()); }}Expliquemos las funciones específicas de varias anotaciones y atributos relacionados en el código:
@Apioperation, toda la configuración del atributo de la interfaz:
Valor: Descripción de la interfaz, que se muestra en la lista de interfaz.
Notas: Descripción detallada de la interfaz, que se muestra en la página Detalles de la interfaz.
Etiquetas: la etiqueta de la interfaz. La interfaz con la misma etiqueta se mostrará en una página de pestaña.
httpmethod: método HTTP compatible.
@ApiimpliceTparams, un contenedor para @apiimpliceTparam, puede contener múltiples anotaciones de @ApiimpliceTparam
@ApiimpliceTparam, Solicitar la configuración del atributo del parámetro:
Nombre: Nombre del parámetro
Valor: descripción del parámetro
Requerido: ¿es necesario
Tipo de datos: tipo de datos
@ApiresponseS, @apiresponse Container, puede contener múltiples anotaciones de @apiresponse
@Apiresponse, devuelva la configuración del atributo de resultados:
Código: Devuelve la codificación del resultado.
Mensaje: Devuelve la descripción del resultado.
Respuesta: Devuelve la clase correspondiente del resultado.
Después de completar la configuración anterior, veamos el efecto de la página:
Página de la lista:
Como puede ver, las interfaces ahora se encuentran en la etiqueta del artículo, y también hay instrucciones para nuestra configuración detrás de la interfaz. Veamos la página Detalles de la interfaz "Post /REST /Artículo":
La imagen es demasiado grande, y solo la visualización del atributo de título se intercepta, y los otros parámetros son similares. Podemos ver en la página que hay instrucciones para los parámetros de solicitud, pero este no es el efecto que esperábamos. Si nuestros parámetros son solo tipos simples, este método debería estar bien, pero el problema ahora es que nuestros parámetros de solicitud son un objeto, entonces, ¿cómo configurarlos? Esto implica otras dos anotaciones: @apimodel y @apimodelproperty. Veamos primero el código y luego explíquelo, lo cual es más fácil de entender:
@Apimodel (valor = "objeto de artículo", descripción = "Agregar y actualizar el objeto del artículo Descripción") Artículo de clase pública {@id @GeneratedValue @apimodelproperty (name = "id", valor = "ID de artículo", requerir = false, ejemplo = "1") ID de larga privada; @Apimodelproperty (name = "title", valor = "título de artículo", requerido = true, ejemplo = "Título de prueba del artículo") Título de cadena privada; @Apimodelproperty (name = "sumario", valor = "resumen de artículo", requerido = true, ejemplo = "prueba de artículo resumen") resumen de cadena privada; @Apimodelproperty (Hidden = true) Fecha privada CreateTime; @Apimodelproperty (Hidden = true) Fecha privada PublicteMe; @Apimodelproperty (Hidden = true) Fecha privada updateTime; @Apimodelproperty (Hidden = True) Private Long UserId; @Apimodelproperty (name = "status", valor = "estado de lanzamiento de artículo", requerido = true, ejemplo = "1") estado entero privado; @Apimodelproperty (name = "type", valor = "categoría de artículo", requerido = true, ejemplo = "1") Tipo de entero privado;}@Apimodel es la configuración de las propiedades de toda la clase:
Valor: Descripción de la clase
Descripción: Descripción detallada
@Apimodelproperty es la configuración de las propiedades de cada campo en detalle:
Nombre: nombre de campo
Valor: descripción del campo
Requerido: ¿es necesario
Ejemplo: valor de ejemplo
Oculto: si mostrar
Después de completar la configuración anterior, veamos el efecto:
Ahora podemos ver que se ha mostrado la descripción del campo, y el valor del campo en el ejemplo también se ha convertido en el valor correspondiente de la propiedad de ejemplo que configuramos. De esta manera, se genera un documento API completo y el documento está estrechamente vinculado al código, en lugar de las dos partes aisladas. Además, también podemos probarlo fácilmente a través de este documento. Solo necesitamos hacer clic en el cuadro amarillo bajo el valor de ejemplo, y el contenido dentro se copiará automáticamente en el cuadro de valor correspondiente del artículo, y luego haga clic en "Pruébelo" para iniciar una solicitud HTTP.
Después de hacer clic, Pruébelo, podemos ver el resultado devuelto:
La operación sigue siendo muy conveniente. En comparación con Junit y Postman, las pruebas a través de Swagger son más convenientes. Por supuesto, las pruebas de Swagger no pueden reemplazar las pruebas unitarias, pero aún tiene un efecto muy importante en la depuración conjunta.
4. Resumen
En general, la configuración de Swagger es relativamente simple, y la capacidad de Swagger para generar documentos automáticamente nos ha ahorrado mucho trabajo y ha proporcionado una gran ayuda para el mantenimiento posterior. Además, Swagger puede generar automáticamente los datos de prueba para nosotros de acuerdo con la configuración y proporcionar los métodos HTTP correspondientes, que también es útil para nuestro trabajo de depuración conjunta y de autocomprobación. Por lo tanto, todavía le recomiendo que use Swagger en el desarrollo diario, lo que debería ayudarlo a mejorar su eficiencia laboral hasta cierto punto. Finalmente, permítanme dejar una pregunta para que piense, es decir, se puede acceder al documento directamente a través de la página, por lo que no podemos exponer la interfaz directamente al entorno de producción, especialmente los sistemas que necesitan proporcionar servicios externos. Entonces, ¿cómo podemos desactivar esta función en el proceso de producción? Hay muchos métodos, puedes probarlo tú mismo.
Lo anterior es el combate real del proyecto Spring Boot Integrated Swagger2 introducido por el editor. Espero que te sea útil. Si tiene alguna pregunta, déjame un mensaje y el editor le responderá a tiempo. ¡Muchas gracias por su apoyo al sitio web de Wulin.com!