Análisis detallado de SpringBoot Http getMapping, postMaping, etc.

fondo

Siempre me ha confundido acerca de cómo la capa del controlador maneja varias solicitudes HTTP. Entonces decidí escribir algunas pruebas yo mismo para resumir.
Entorno del proyecto: springBoot, arrogancia.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>
<!--swagger-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
    <exclusions>
        <exclusion>
            <artifactId>swagger-models</artifactId>
            <groupId>io.swagger</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>1.5.24</version>
    <exclusions>
        <exclusion>
            <artifactId>swagger-annotations</artifactId>
            <groupId>io.swagger</groupId>
        </exclusion>
    </exclusions>
</dependency>

Contenido resumido:
Anotaciones sobre los métodos: @postMapping, @GetMapping, @PutMapping, @DeleteMapping, @PatchMapping (usados ​​para algunas modificaciones, pero básicamente no usados).
Anotaciones sobre parámetros: @PathVariable, @RequestBody, @RequestPart.
Decidió explicar el uso en función de las necesidades reales.

Notas sobre los parámetros

@PathVariable

La traducción al chino es parámetros de ruta. La anotación @PathVariable puede vincular los parámetros del marcador de posición en la URL a los parámetros de entrada del método de procesamiento del controlador; se utiliza principalmente para recibir datos http://host:port/path/{parameter value} y realizar el estilo RestFul. convertir parámetros chinos a hexadecimal.

/**
     * 主要用于接收http://host:port/path/{参数值}数据
     * 实现了RestFul的风格
     * 中文参数会转成16进制
     * @param name
     * @return
     */
    @ApiOperation("PathVariable传参")
    @GetMapping("/get/{name}")
    public ResponseVo<String> pathVariable(@ApiParam(value = "你的名字") @PathVariable String name ){
    
    
        return ResponseFactory.success(name);
    }

Mostrado con arrogancia:
Insertar descripción de la imagen aquí

Nota: "ruta" se muestra debajo de los parámetros; Li Jinrong la convirtió a %E6%9D%8E%E9%94%A6%E8%8D%A3; la ventaja de PathVariable es que además de realizar el estilo restFul, no No sé cuáles son los beneficios por el momento (si sabes algo, dímelo en el área de comentarios, gracias)

Adivina: si hay caracteres especiales en los parámetros, como /, ¿se verá afectado?

Los parámetros son los siguientes: "/李jinrong" y se devuelve el mensaje de error: Estado HTTP 400: Solicitud incorrecta:

<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {
     
     font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {
     
     color:white;background-color:#525D76;} h1 {
     
     font-size:22px;} h2 {
     
     font-size:16px;} h3 {
     
     font-size:14px;} p {
     
     font-size:12px;} a {
     
     color:black;} .line {
     
     height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>

Demuestra que @pathVariable no es adecuado para pasar caracteres especiales. Si debe usarlo, puede consultar el artículo "Lo que necesita saber sobre @PathVariable". Dirección: https://www.jianshu.com/p/1a1384b9cd34

Adivina: ¿Se puede implementar una verificación no nula de parámetros?

1. Intente agregar
la capa de controlador @PathVariable(required = true):

    /**
     * PathVariable能否实现对参数的非空校验
     * 1.尝试加入@PathVariable(required = true)
     * @param name
     * @return
     */
    @ApiOperation("PathVariable传参")
    @GetMapping("/get/{name}/1")
    public ResponseVo<String> pathVariable2(
            @ApiParam(value = "你的名字") @PathVariable(required = true) String name ){
    
    
        return ResponseFactory.success(name);
    }

Swagger no ingresa parámetros, hace clic en Ejecutar e
Insertar descripción de la imagen aquí
ingresa exitosamente el método. Los parámetros no están definidos, lo que indica que @PathVariable (requerido = verdadero) no puede realizar la verificación.
Insertar descripción de la imagen aquí

2. Intente agregar @NotNull

/**
     * PathVariable能否实现对参数的非空校验
     * 2.尝试加入@NotNull
     * @param name
     * @return
     */
    @ApiOperation("PathVariable传参")
    @GetMapping("/get/{name}/2")
    public ResponseVo<String> pathVariable3(
            @ApiParam(value = "你的名字") @PathVariable(required = true) @NotNull String name ){
    
    
        return ResponseFactory.success(name);
    }

resultado. Aún sin definir:
Insertar descripción de la imagen aquí
Agregar @NotEmpty - no válido; agregar @Length (max = 3) - válido
Conclusión: La conclusión extraída de la prueba anterior es que @PathVariable no puede realizar una verificación de parámetros no vacíos.

SolicitudParam

Se utiliza principalmente para recibir http://host:puerto/ruta?Nombre del parámetro = datos del valor del parámetro (diferente del formato @PathVariable)
Los parámetros anotados @RequestParam se pasan en la URL.
@RequestParam es adecuado para anotar int y string, porque el front-end lo pasa al backend y, en última instancia, estos dos tipos son los únicos. También puede entenderse como una anotación únicamente del formato de parámetro de los pares clave-valor.
@RequestParam no se aplica a entidades anotadas (DTO, etc.).

/**
     * 主要用于接收http://host:port/path?参数名=参数值数据(与@PathVariable格式不同)
     * 适用于注解int和string,因为前端传给后台,最终就这两个类型
     * 不适用注解实体(DTO等)
     * @param name
     * @return
     */
    @ApiOperation("RequestParam传参")
    @GetMapping("/get/requestParam")
    public ResponseVo<String> requestParam(
            @ApiParam(value = "你的名字") @RequestParam(required = true) String name ){
    
    
        return ResponseFactory.success(name);
    }

Se muestra con arrogancia:
Insertar descripción de la imagen aquí
Nota: Aquí puede ver que RequestParam muestra la consulta y no hay ningún problema con los caracteres especiales. @RequestParam(required = true) por defecto es verdadero y verificará que no esté vacío. No lo demostraré aquí, todos deberían saberlo.

Verifique que @RequestParam no se aplique a entidades anotadas (DTO, etc.)

/**
     * RequestParam注解DTO实体会报String无法转为DTO的错误
     * @param dogDTO
     * @return
     */
    @ApiOperation("RequestParam传参")
    @GetMapping("/get/requestParam1")
    public ResponseVo<String> requestParam1(
            @ApiParam(value = "你的名字") @RequestParam DogDTO dogDTO ){
    
    
        return ResponseFactory.success(dogDTO.getDogName());
    }

Insertar descripción de la imagen aquí
Se informa un error después de la ejecución: No se puede convertir el valor del tipo 'java.lang.String' al tipo requerido 'com.aliyu.entity.demo.dto.DogDTO':
Nota: En el diagrama de swagger, puede ver que el tipo de parámetro es una cadena en lugar de la entidad dto correspondiente.

@RequestBody

1. Los parámetros se colocan en el cuerpo. El tipo de contenido predeterminado enviado por el front-end es aplicación/json;
2. El tipo de contenido es aplicación/x-www-form-urlencoded, que no se admite en circunstancias normales. Por supuesto que hay otras soluciones, pero requieren añadir algo. Dirección de referencia: https://blog.csdn.net/weixin_32836221/article/details/112111893;3
. Otros formatos incluyen application/json, application/xml, etc. Los datos en estos formatos deben procesarse usando @RequestBody
4. Los datos multiparte/formulario no se pueden procesar, son manejados por @RequestPart, lo cual se discutirá más adelante.
En pocas palabras, si el parámetro es json, use @RequestBody; si se usa @RequestBody, se especifica que el parámetro es json.

@ApiOperation("post请求-DTO实体-RequestBody传参数")
    @PostMapping("/post/requestBody")
    public ResponseVo requestBody(@RequestBody DogDTO dogDTO) {
    
    
        return ResponseFactory.success(dogDTO.getDogName());
    }

Insertar descripción de la imagen aquí

Demuestra que el tipo de contenido está codificado en la aplicación/x-www-form-urlen y no es compatible en circunstancias normales.

Para demostrar esto, se necesita un punto de conocimiento sobre cómo limitar el tipo de contenido de los parámetros pasados ​​por el front-end.

/**
     * 反例:Content-Type为application/x-www-form-urlencoded,正常情况不支持
     * 报错Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
     * consumes设置前端传入的参数的contentType
     * produces设置返回的contentType
     * @param dogDTO
     * @return
     */
    @ApiOperation("RequestBody接收contentType为application/x-www-form-urlencoded")
    @PostMapping(value="/post/requestBody2", consumes = {
    
    MediaType.APPLICATION_FORM_URLENCODED_VALUE} )
    public ResponseVo requestBody2(@RequestBody DogDTO dogDTO) {
    
    
        return ResponseFactory.success(dogDTO.getDogName());
    }

Nota: Hay dos configuraciones en postMapper, una consume conjuntos del tipo de contenido de los parámetros pasados ​​por el front-end y produce conjuntos del tipo de contenido devuelto. El valor predeterminado devuelto es json y no es necesario configurarlo.
A continuación, mire la pantalla con arrogancia:
Insertar descripción de la imagen aquí
el tipo de contenido se configuró correctamente en aplicación/x-www-form-urlencoded y la solicitud es:
Insertar descripción de la imagen aquí
error Tipo de contenido 'application/x-www-form-urlencoded;charset=UTF-8' No soportado.

Demuestre que los datos multiparte/formularios no se pueden procesar

/**
     * 反例子
     * 证明RequestBody不支持接收contentType为multipart/form-data
     * @param dogDTO
     * @return
     */
    @ApiOperation("RequestBody接收contentType为multipart/form-data")
    @PostMapping(value = "/post/requestBody3",consumes = {
    
    MediaType.MULTIPART_FORM_DATA_VALUE})
    public ResponseVo requestBody3(@RequestBody DogDTO dogDTO) {
    
    
        return ResponseFactory.success(dogDTO.getDogName());
    }

Insertar descripción de la imagen aquí
El tipo de contenido se configuró correctamente en multipart/form-data. Échale un vistazo:
Insertar descripción de la imagen aquí

@RequestPart

Esta anotación establece el tipo de contenido en multipart/form-data, que es adecuado para cargar archivos
. Esto también debe usarse al cargar archivos e ingresar parámetros json al mismo tiempo. Sin embargo, cuando se utiliza @RequestPart para anotar parámetros json, se informará un error en circunstancias normales.
Anotar int o cadena, archivo de anotaciones:

/**
     * 这个注解设定contentType为multipart/form-data,适用于文件上传
     * @param dogName
     * @param file
     * @return
     */
    @ApiOperation("RequestPart上传文件,@RequestPart注解string或int参数")
    @PostMapping("/post/requestPart")
    public ResponseVo requestPart(@RequestPart String dogName,
                                  @RequestPart MultipartFile file) {
    
    
        return ResponseFactory.success(dogName);
    }

@RequestPart corresponde a formData mostrado:
Insertar descripción de la imagen aquí
contentType es multipart/form-data:
Insertar descripción de la imagen aquí
después de hacer clic para ejecutar, el backend informa un error:
com.fasterxml.jackson.core.JsonParseException: token no reconocido 'AAA': se esperaba (cadena JSON, número, Matriz, objeto o token 'null', 'true' o 'false')
en [Fuente: (PushbackInputStream); línea: 1, columna: 4]
significa que este tipo de parámetro no se puede analizar. Los tipos admitidos son "Cadena JSON, Número, matriz, objeto o token 'nulo', 'verdadero' o 'falso'". Es decir, @RequestPart String dogName en la capa del controlador no puede usar cadenas, pero debe usar DTO.
Si el parámetro de anotación es DTO plus file

/**
     * 注解一个dto和一个文件
     * @param dto
     * @param file
     * @return
     */
    @ApiOperation("RequestPart上传文件,@RequestPart注解string或int参数")
    @PostMapping("/post/requestPart2")
    public ResponseVo requestPart2(@RequestPart DogDTO dto,
                                  @RequestPart MultipartFile file) {
    
    
        return ResponseFactory.success(dto);
    }

Insertar descripción de la imagen aquí
Haga clic para ejecutar y se informa un error: HttpMediaTypeNotSupportedException: el tipo de contenido 'application/octet-stream' no es compatible
Dirección de referencia: https://blog.csdn.net/gao_grace/article/details/96431269
El significado aproximado es: use la API llamado por swagger, verifique El comando curl generado por swagger encontró que el parámetro de archivo en la solicitud especificaba el tipo de contenido, pero dto no. El backend no puede encontrar el tipo de contenido, por lo que tiene que utilizar la aplicación/flujo de octeto predeterminado. Sin embargo, no existe un conversor de mensajes para el tipo correspondiente, por lo que se informa que no es compatible.
Creo que la declaración puede ser inexacta, porque una solicitud solo puede tener un tipo de contenido, no un parámetro configurado una vez. Sin embargo, el DTO necesita configurar su tipo de datos en json para que esto funcione. Sin embargo, Swagger actualmente no admite la configuración y la interfaz real existe (no estoy familiarizado con el código de la interfaz, si lo conoce, puede proporcionar un ejemplo).
La solución es escribir una clase convertidora anotada @component, de la siguiente manera:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;
/**
 *@author: aliyu
 *@create: 2021/1/14 10:35
 *@description: 增加对application/octet-stream的消息转换器
 * 用@RequestPart同时上传文件和json时,传入的contentType为null,导致使用默认的application/octet-stream
 * 而application/octet-stream没有对应的消息转换器
 */
@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    
    
    protected MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
    
    
        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    
    
        return false;
    }

    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
    
    
        return false;
    }

    @Override
    protected boolean canWrite(MediaType mediaType) {
    
    
        return false;
    }
}

Después de reiniciar el proyecto, los parámetros se pueden pasar normalmente.

Consulta - @GetMapping

Se utiliza principalmente para consultas.
Obtener características de la solicitud:
a. Los parámetros de la solicitud se agregarán al final de la ruta del recurso solicitado y solo se pueden agregar una pequeña cantidad de parámetros (porque la línea de solicitud tiene solo una línea, que solo puede almacenar aproximadamente 2K de datos) b Los
parámetros de la solicitud se mostrarán en la barra de direcciones del navegador y el enrutador registrará la dirección de la solicitud (extremadamente inseguro).

Consultas con muy pocos parámetros, los parámetros de consulta no contienen clases de entidad (solo tipos básicos)

No debe haber más de 3 parámetros de consulta. En la mayoría de los casos, los detalles se obtienen mediante ID. Si son más de 3, se recomienda crear un nuevo DTO para almacenarlos.

@ApiOperation("基本类型参数,少量参数传参")
    @GetMapping("/get/getMapping")
    public ResponseVo<String> getMapping(
            @ApiParam(value = "你的名字") @RequestParam String name,
            @ApiParam(value = "性别") @RequestParam String sex,
            @ApiParam(value = "年龄") @RequestParam Integer age){
    
    
        return ResponseFactory.success(name);
    }

Insertar descripción de la imagen aquíNota: Los parámetros se pueden obtener normalmente después de la ejecución. ¿Por qué no usar @PathVariable? Porque, como se mencionó anteriormente, no admite la verificación de parámetros no vacíos y no se pueden ingresar caracteres especiales como "/". Está bien si los parámetros no agregan @RequestParam, pero no es necesario perderlos (porque RequestParam debe perderse de forma predeterminada). Si el parámetro no está vacío, se debe agregar esta anotación.

El parámetro de consulta es DTO

En escenarios empresariales, la consulta de un módulo suele ser algunos campos de una determinada clase de entidad, si hay muchos campos, o se considera la escalabilidad. Deberías crear un DTO para aprobarlo. Para solicitudes de obtención, no se puede utilizar @ResponseBody. En este momento, este dto se puede utilizar sin agregar ninguna anotación.

/**
     * get请求dto传参。
     * @param dto
     * @return
     */
    @ApiOperation("dto传参")
    @GetMapping("/get/getMapping2")
    public ResponseVo<String> getMapping2(DogDTO dto){
    
    
        return ResponseFactory.success(dto.getDogName());
    }

Insertar descripción de la imagen aquí
Nota: Los parámetros de consulta se pasan sin valor predeterminado. En cuanto a la verificación de los parámetros dto, se implementa agregando anotaciones de verificación a cada atributo en la clase de entidad dto (puede consultar mi otro artículo de verificación)

Nuevo - @PostMapping

Idempotencia: significa que una o varias solicitudes de un recurso deben tener el mismo resultado para el recurso en sí (excepto por problemas como el tiempo de espera de la red).
En otras palabras, el impacto de cualquier ejecución múltiple en el recurso en sí es el mismo que el impacto de una ejecución. El
protocolo http estipula claramente que las solicitudes de colocación, obtención y eliminación son idempotentes, mientras que la publicación no es idempotente.

Prefiero usar
a al agregar. Los parámetros de solicitud se agregan al contenido de la entidad y se pueden agregar una gran cantidad de parámetros (esto también explica por qué la barra de direcciones del navegador no puede enviar solicitudes de publicación. Solo podemos completar la URL en el barra de direcciones y no puede ingresar al paquete Http. Entre las entidades)
b. Es relativamente seguro que get. Sin embargo, la solicitud de publicación no cifrará los parámetros de la solicitud (se puede usar el protocolo https para garantizar la seguridad de los datos).

Se agregan pocos parámetros nuevos (los parámetros son solo tipos básicos, no recomendados)

Esta situación es relativamente rara. Generalmente, se agregará un nuevo dato. Los campos de la tabla generalmente no son demasiado pocos, por lo que los parámetros que se ingresarán al agregar generalmente no son demasiado pocos.

@ApiOperation("post请求-少量参数-RequestParam传参")
    @PostMapping("/post/postMappring")
    public ResponseVo postMappring(@ApiParam(value = "你的名字") @RequestParam String dogName,
                                   @ApiParam(value = "年龄") @RequestParam int age,
                                   @ApiParam(value = "性别") @RequestParam String sex) {
    
    
        return ResponseFactory.success(dogName);
    }

Insertar descripción de la imagen aquí
Nota: Puede ver que dichos parámetros en realidad se almacenan en la URL, que es casi lo mismo que obtener. . . Por eso no se recomienda escribir así.
Además, si se crea un @RequestBody con un parámetro, se informará el error "Error de E/S al leer el mensaje de entrada; la excepción anidada es java.io.IOException: flujo cerrado". Una persona increíble escribió "La postura correcta para usar múltiples @RequestBody en el controlador SpringBoot", dirección: https://blog.csdn.net/w605283073/article/details/82119284

Resumen: según lo anterior, se recomienda utilizar dto incluso si solo hay uno o dos parámetros.

El parámetro es DTO.

Agregue todos los parámetros requeridos en un dto, excluyendo los archivos.

@ApiOperation("传参dto")
    @PostMapping("/post/postMappring4")
    public ResponseVo postMappring4(@RequestBody DogDTO dto) {
    
    
        return ResponseFactory.success(dto.getDogName());
    }

Insertar descripción de la imagen aquí

El parámetro es el archivo DTO plus.

Al cargar archivos, solo puede anotar el archivo con @RequestPart y el dto también debe anotarse con @RequestPart. Esto se debe a que @RequestPart y @RequestBody están presentes. @RequestPart tiene una prioridad más alta y establecerá el tipo de contenido en multipart/form-data. Pero como se mencionó anteriormente, @RequestBody no admite datos multiparte/formulario.

/**
     * 注解一个dto和一个文件
     * @param dto
     * @param file
     * @return
     */
    @ApiOperation("RequestPart注解上传文件和DTO")
    @PostMapping("/post/requestPart5")
    public ResponseVo postMappring5(@RequestPart DogDTO dto,
                                   @RequestPart MultipartFile file) {
    
    
        return ResponseFactory.success(dto);
    }

Insertar descripción de la imagen aquí
Los datos se pueden obtener normalmente:
Insertar descripción de la imagen aquí

Modificación - @PutMapping

Los parámetros son tipos básicos.

Utilice @RequestParam

@ApiOperation("put请求-单个参数RequestParam、文件上传")
@PutMapping("/put/putMapping")
public ResponseVo putMapping(@RequestParam String dogName,
                                @RequestPart MultipartFile file) {
    
    
    return ResponseFactory.success(dogName);
}

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
Nota: No hay diferencia con la publicación, no se requiere explicación especial

El parámetro es el archivo DTO plus.

@ApiOperation("put请求-DTO加文件上传")
    @PutMapping("/put/putMapping2")
    public ResponseVo putMapping2(@RequestPart DogDTO dto,
                                 @RequestPart MultipartFile file) {
    
    
        return ResponseFactory.success(dto.getDogName());
    }

Nota: No hay diferencia con la publicación, no se requiere explicación especial

Eliminar - @DeleteMapping

A través de parámetros de tipo básico (menos parámetros)

Método PathVariable

/**
 * 单个参数
 * @param id
 * @return
 */
@DeleteMapping("/deleteMapping/{id}")
public ResponseVo deleteMapping(
@ApiParam(required = false, value = "主键id") @PathVariable int id) {
    
    
    return ResponseFactory.success(id);
}

Insertar descripción de la imagen aquí
Nota: El parámetro de la anotación PathVariable puede ser nulo al obtenerMapping, pero no al @DeleteMapping. En este momento, su identificación predeterminada debe tener un valor. Entonces, si está seguro de que no hay caracteres especiales en la identificación y debe haber un valor, puede usar este método.

Método RequestParam

@DeleteMapping("/deleteMapping/{id}/1")
    public ResponseVo deleteMapping3(@PathVariable int id,
     @RequestParam String name) {
    
    
        return ResponseFactory.success(id + name);
    }

Insertar descripción de la imagen aquí

Vía DTO

/**
     * PathVariable+RequestParam+RequestBody
     * @param id
     * @return
     */
    @DeleteMapping("/deleteMapping/{id}/2")
    public ResponseVo deleteMapping2(@PathVariable int id, 
                                     @RequestParam String name, 
                                     @RequestBody DogDTO dto) {
    
    
        return ResponseFactory.success(id + name+ dto.getDogName());
    }

Insertar descripción de la imagen aquí
Nota: Es extraño, pensé que sería lo mismo que getMaping, pero RequestBody no se puede usar.
No esperaba que fuera posible.
Insertar descripción de la imagen aquí

Resumir

Dado que http distingue cuatro tipos de mapeo para escenarios comerciales de agregar, eliminar, modificar y verificar, naturalmente tiene su propósito. Simplemente no estoy familiarizado con Internet, así que no lo entiendo y casi no hay explicación sobre Baidu. Pero no hay problema si lo sigues. Al escribir postMapping, mencioné la idempotencia. Supongo que http puede tener una optimización personalizada según la operación específica de la base de datos solicitada.

Supongo que te gusta

Origin blog.csdn.net/mofsfely2/article/details/112603657
Recomendado
Clasificación