首页 > 其他分享 >由 RequestBody 注解想到的

由 RequestBody 注解想到的

时间:2023-12-20 23:05:00浏览次数:32  
标签:请求 form 数据 想到 表单 RequestBody multipart 注解 data

近日初学 SpringBoot 框架,Post 一个接口,返回了:

2023-12-19T15:25:38.728+08:00  WARN 23508 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'multipart/form-data;boundary=--------------------------692495610520047406709727;charset=UTF-8' is not supported]

我的接口是这样的:

@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(
    @RequestBody RegisterRequest request
) {
  return ResponseEntity._ok_(service.register(request));
}

其中RegisterRequest为:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RegisterRequest {

  private String firstname;
  private String lastname;
  private String email;
  private String password;
}

请求为:

curl --location 'http://127.0.0.1:8080/api/v1/auth/register' \
--form 'firstname="zz"' \
--form 'lastname="zz"' \
--form 'email="[email protected]"' \
--form 'password="123456"'

看了下 Content-Type,根据我的表单输入,默认设置为了 multipart/form-data; boundary=calculated when request is sent

当即意识到应该用 Json 在 POST 的 payload 里面传递这些数据,像下面这样:

curl --location 'http://127.0.0.1:8080/api/v1/auth/register' \
--header 'Content-Type: application/json' \
--data-raw '{
  "firstname": "zz",
  "lastname": "zz",
  "email": "[email protected]",
  "password": "123456"
}'

但是,我就在想,为啥我使用表单传递数据,后端就会拒绝呢?它是怎么判断我传递的什么呢?使用表单和 JSON 有啥区别呢?

先来看后端是怎么判断的。

我们先看下 @RequestBody 注解的源码

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.http.converter.HttpMessageConverter;

_/**_
_ * Annotation indicating a method parameter should be bound to the body of the web request._
_ * The body of the request is passed through an {_**@link **_HttpMessageConverter} to resolve the_
_ * method argument depending on the content type of the request. Optionally, automatic_
_ * validation can be applied by annotating the argument with {_**@code **_@Valid}._
_ *_
_ * <p>Supported for annotated handler methods._
_ *_
_ * _**@author **_Arjen Poutsma_
_ * _**@since **_3.0_
_ * _**@see **_RequestHeader_
_ * _**@see **_ResponseBody_
_ * _**@see **_org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter_
_ */_
@Target(ElementType._PARAMETER_)
@Retention(RetentionPolicy._RUNTIME_)
@Documented
public @interface RequestBody {

   _/**_
_    * Whether body content is required._
_    * <p>Default is {_**@code **_true}, leading to an exception thrown in case_
_    * there is no body content. Switch this to {_**@code **_false} if you prefer_
_    * {_**@code **_null} to be passed when the body content is {_**@code **_null}._
_    * _**@since **_3.2_
_    */_
_   _boolean required() default true;

}

我们看到了关键的一句话:The body of the request is passed through an {**@link **_HttpMessageConverter} to resolve the method argument depending on the content type of the request. _

他会根据请求的_content type,_去找到对应的 Converter,那我的原来的请求的 Content-Type 应该为 multipart/form-data,所以它应该去找 HttpMessageConverter 借口 form 相关的实现类

file 找到了,看下注释:

Implementation of HttpMessageConverter to read and write 'normal' HTML forms and also to write (but not read) multipart data (e.g. file uploads).

In other words, this converter can read and write the "application/x-www-form-urlencoded" media type as MultiValueMap<String, String>, and it can also write (but not read) the "multipart/form-data" and "multipart/mixed" media types as MultiValueMap<String, Object>.

总而言之,就是它会把 application/x-www-form-urlencoded 类型的数据转换为 MultiValueMap<String, String>,把 multipart/form-data" and "multipart/mixed"类型的,转换为 MultiValueMap<String, Object>。

所以我们的请求理论上会在这里被转换。后面的注释就还是在解释 content type 相关的内容。

By default, "multipart/form-data" is used as the content type when writing multipart data. As of Spring Framework 5.2 it is also possible to write multipart data using other multipart subtypes such as "multipart/mixed" and "multipart/related", as long as the multipart subtype is registered as a supported media type and the desired multipart subtype is specified as the content type when writing the multipart data. Note that "multipart/mixed" is registered as a supported media type by default.

When writing multipart data, this converter uses other HttpMessageConverters to write the respective MIME parts. By default, basic converters are registered for byte array, String, and Resource. These can be overridden via setPartConverters or augmented via addPartConverter.

总之,这个 HttpMessageConverter 实现的作用是处理 HTML 表单数据的读写,同时支持处理多部分数据,例如文件上传。在写入多部分数据时,可以选择不同的多部分子类型,并且可以定制处理多部分数据的具体方式。知道有这么个东西就行。

由此可以看到,原生的 form 表单可以传递很多类型的数据?如何使用 HTTP 实现文件上传?这里我们留个坑,后面再探究。

我们回来继续看刚开始遇到的问题,我是用表单传输了 key:value 类型的数据

--form 'firstname="zz"' \
--form 'lastname="zz"' \
--form 'email="[email protected]"' \
--form 'password="123456"'

这种其实应该使用 @RequestParam 注解

@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(
    @RequestParam("firstname") String firstname,
    @RequestParam("lastname") String lastname,
    @RequestParam("email") String email,
    @RequestParam("password") String password
) {
    // 在这里处理注册逻辑,使用接收到的参数进行操作
    // ...

    return ResponseEntity.ok(/* 返回响应 */);
}

是不是感觉怎么和 URL 里面携带的请求参数有点像啊?

HTML 的原生表单可以使用 POST 和 GET 方法。这两种方法指定了在提交表单时浏览器使用的 HTTP 请求方法。

  • POST 方法: 通过 POST 方法提交表单时,表单数据会被包含在 HTTP 请求的消息体中,而不是作为 URL 的一部分。这通常用于向服务器提交较大量的数据,或包含敏感信息的数据,因为 POST 方法将请求体中的数据加密,而 URL 参数是以明文形式传输的。
htmlCopy code
<form method="post" action="/submit"<!-- 表单内容 --></form>
  • GET 方法: 通过 GET 方法提交表单时,表单数据会以查询字符串的形式附加在 URL 后面。这种方法适用于提交较小的数据,且数据不包含敏感信息。由于数据以明文形式附加在 URL 中,因此不建议用于包含敏感信息的数据。
htmlCopy code
<form method="get" action="/submit"<!-- 表单内容 --></form>

开发者可以根据实际需求选择适当的提交方法。在处理用户登录等敏感操作时,通常会使用 POST 方法,而在搜索等不涉及敏感信息的场景中,可以使用 GET 方法。

那 URL 上携带的请求参数,应该使用什么注解处理呢?

URL 上携带的请求参数可以使用 @RequestParam 注解来处理。这个注解用于将 HTTP 请求的查询参数(query parameters)映射到方法的参数上。以下是一个简单的例子:

@GetMapping("/example")
public ResponseEntity<String> example(
    @RequestParam(name = "param1", defaultValue = "default-value") String param1,
    @RequestParam(name = "param2", required = false) Integer param2
) {
    // 在这里处理逻辑,使用接收到的参数进行操作
    // ...

    return ResponseEntity.ok(/* 返回响应 */);
}

在这个例子中,param1 是一个必须的参数,如果请求中没有提供,则会使用默认值。而 param2 是一个可选的参数,通过 required = false 表示,如果请求中没有提供该参数,它将为 null

总结 @RequestBody 注解

@RequestBody 注解是 Spring 框架中的注解,用于标识一个方法参数应该绑定到 Web 请求的主体(body)。它告诉 Spring 框架,将请求的内容体转换为方法参数的类型。这个注解主要用于处理 HTTP 请求的 POST、PUT 等方法,其中请求的数据是通过请求体(body)发送的,而不是作为 URL 参数。

具体流程如下:

  1. 当一个请求到达 Spring MVC 控制器的方法时,Spring 框架会检查该方法的参数上是否有 @RequestBody 注解。
  2. 如果发现有 @RequestBody 注解,Spring 框架会使用合适的 HttpMessageConverter 将请求体的内容转换成方法参数的类型。HttpMessageConverter 是一个接口,定义了如何读取请求体并写入响应体。
  3. 转换后的数据会被传递给方法参数,然后你就可以在方法中处理这些数据了。

总的来说,@RequestBody 注解告诉 Spring 框架将请求体的内容绑定到方法的参数上,具体的绑定方式则由 HttpMessageConverter 实现。这使得开发者能够方便地处理各种不同类型的请求数据,例如 JSON、XML 等。

在使用 @RequestBody 注解时,一般需要搭配相应的消息转换器(HttpMessageConverter),这样 Spring 框架才能正确地解析请求体中的数据。默认情况下,Spring 提供了一些常见的消息转换器,例如处理 JSON 数据的 MappingJackson2HttpMessageConverter

表单数据通过 multipart/form-data 和 application/x-www-form-urlencoded 传输,有啥区别?

multipart/form-dataapplication/x-www-form-urlencoded 是两种用于发送表单数据的不同 Content-Type 类型。它们的主要区别在于数据的编码方式和适用场景:

  1. application/x-www-form-urlencoded: 这是默认的表单数据编码类型。在这种编码方式下,表单数据被编码为键值对的形式,每个键值对之间使用 "&" 符号分隔,并且空格会被编码为 "+" 或 "%20"。这种方式适用于普通的表单提交。
  2. 例如,键值对 "key1=value1&key2=value2" 表示两个表单字段的数据。
  3. multipart/form-data: 这种编码方式适用于包含文件上传的表单。在这种情况下,表单数据被划分为多个部分,每个部分都有一个唯一的标识符,同时也包含了数据的类型信息。这样可以方便地上传二进制文件等数据。
  4. 这种方式的请求头会包含一个 boundary 参数,用于分隔不同的部分。
  5. 例如,一个包含文本字段和文件上传字段的 multipart/form-data 请求可能看起来像这样:

总体而言,application/x-www-form-urlencoded 适用于普通表单提交,而 multipart/form-data 适用于包含文件上传的场景。

本文由博客一文多发平台 OpenWrite 发布!

标签:请求,form,数据,想到,表单,RequestBody,multipart,注解,data
From: https://blog.51cto.com/u_16458956/8911154

相关文章

  • SpringBoot 注解详解
    1.注解详解@SpringBootApplication:申明让springboot自动给程序进行必要的配置,这个配置等同于:@Configuration,@EnableAutoConfiguration和@ComponentScan三个配置。@ResponseBody:表示该方法的返回结果直接写入HTTPresponsebody中,一般在异步获取数据时使用,用于构建RESTful的a......
  • Spring Boot学习随笔- 实现AOP(JoinPoint、ProceedingJoinPoint、自定义注解类实现切面
    学习视频:【编程不良人】2021年SpringBoot最新最全教程第十一章、AOP11.1为什么要使用AOP问题现有业务层开发存在问题额外功能代码存在大量冗余每个方法都需要书写一遍额外功能代码不利于项目维护Spring中的AOPAOP:Aspect切面+Oriented面向Programmaing......
  • 注解
    可以使用注解来保存类的相关信息以供反射调用1、简介提供一种为程序元素设置元数据的方法元数据是添加到程序元素(如:方法、字段、类和包)上的额外信息注解是一种分散式的元数据设置方式,XML是集中时的设置方式注解不能直接干扰程序代码的运行2、功能作为特定的标记,用于告......
  • 第三章:@RequestMapping注解
    一、搭建框架二、控制器中有多个方法对应同一个请求的情况三、@RequestMapping注解标识的位置四、@RequestMapping注解的value属性五、@RequestMapping注解的method属性六、@RequestMapping注解结合请求方式的派生注......
  • 总结篇:SpringBoot常用注解总结
    使用springboot开发的优点,就是不用部署war文件因为内部嵌入了tomcat的,允许通过maven来根据需要的starter,非常的方便,可以自动配置spring,为程序员减少大量时间用于写业务逻辑,更不用担心使用某个依赖的版本问题,springboot全部为你自己选择。springboot的常用注解:1、@SpringBootAppl......
  • Spring中关于@Autowired注解和@Value注解的处理
    Spring是在实例化之后、初始化之前调用BeanPostProcessor处理的。/////////////////////////////////////////////////////////////////////////////AbstractApplicationContext>>>>refresh()///////////////////////////////////////////////////////////////////////////......
  • mybatis 注解开发
    注解开发<mappers><!--直接读取映射文件--><!--<mapperresource="kong/UserMapper.xml"/>--><!--获取dao文件xml文件存放的路径和dao接口的包名要对应--><packagename="com.kong.dao"/>......
  • Android开发——组合函数、注解与连接Android设备
    1、JetPackCompose、组合函数与注解和文本修改1、JetPackCompose:JetpackCompose是由Google推出的用于构建Android用户界面的现代化工具包。它是一个声明式的UI工具包,用于简化Android应用程序的用户界面设计和开发。JetpackCompose采用了类似于React或Flutter的......
  • 第五章:SpringMVC的常用注解
    一、21、springMVC常用注解二、springMVC相关注解理论整合1、@RequestMapping:用来处理请求地址映射的注解,可用于类或方法上@RequestMapping("/path")表示该控制器处理所有"/path"的URL请求。用于类上,表示类中的所有响应请求的方法都是以该地址作......
  • C#注释:提升可读性的注解艺术
    文章目录注释行注释段注释XML注释一级注释二级注释注释换行TODO注释注释行注释//注释内容段注释/*注释内容*/XML注释///<summary>///注释内容///</summary>///是智能注释也称xml注释,会在被编译,并生成xml文件在可执行文件中。会影响编译速度,但不......