Controller中常用的注解
@RestController() 和 @Controller()
@RestController 注解将 @Controller 和 @ResponseBody 注解合二为一,这会应用于该类中定义的所有端点
@ResponseBody 和 ResponseEntity
@ResponseBody是一个Spring MVC注解,用于指示方法的返回值应该被直接写入HTTP响应体。这通常用于处理返回非HTML内容的请求,如JSON或XML。
@GetMapping("/users/{userId}")
@ResponseBody
public User getUser(@PathVariable Long userId) {
// 根据userId获取用户信息
return userService.getUserById(userId);
}
在这个例子中,@ResponseBody注解确保了方法返回的User对象被直接序列化为JSON或XML格式,并写入HTTP响应体中。
@ResponseBody注解的核心在于其与Spring MVC的HttpMessageConverter接口的协同工作,该接口定义了返回值与HTTP响应体之间的转换逻辑。
与 @ResponseBody 相反,ResponseEntity 是一种通用类型,代表整个 HTTP 响应。因此,可以控制其中的任何内容:HTTP状态码、HTTP Header 和 HTTP Body。
设置自定义标头:
@GetMapping("/customHeader")
ResponseEntity<String> customHeader() {
return ResponseEntity.ok()
.header("Custom-Header", "foo")
.body("Custom header set")
如果将一个对象放入:
@GetMapping("/hello")
public ResponseEntity<String> hello() {
return new ResponseEntity<>(new User(‘jdon’), HttpStatus.OK);
}
Consumes & Produces & Accept & Content-Type
上面的4个属性,首先分为2类:
- Accept 和 Content-Type 属于 Client方配置的header;
- Consumes 和 Produces 属于Server方配置的header;
consumes 属性指定 Controller 可接受的媒体类型,可以有多个。
@RequestMapping(value = "/greetings", method = RequestMethod.POST, consumes="application/json")
public void addGreeting(@RequestBody ContentType type, Model model) {
}
produces 指定 Controller 发送回客户端的媒体类型,这也是一个列表,可以指定多个。如果系统不能按照这个媒体类型响应资源,则会响应 “406 Not Acceptable” 错误。
@GetMapping(
value = "/greetings-with-response-body",
produces="application/json"
)
public String getGreetingWhileReturnTypeIsString() {
return "{\"test\": \"Hello\"}";
}
Accept代表发送端(客户端)希望接受的数据类型。
比如:Accept:text/xml;
代表客户端希望接受的数据类型是xml类型
Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型。
比如:Content-Type:text/html;
代表发送端发送的数据格式是html。
produces如何起作用的呢?
结论:设置了produces后,在响应头的content-type中,就会写入相应的值
看一个示例:
@GetMapping(value = "/sse",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> sse() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String>builder()
.id(String.valueOf(sequence))
.event("periodic-event")
.data("Current time: " + LocalTime.now())
.build()).take(5);
}
接下来,在响应头中,Content-Type的值,就会是text/event-stream;charset=UTF-8,这个就表示是sse流
下面,进入理论学习
在Spring Boot中,@RequestMapping
注解的produces属性主要用于指定控制器方法返回的响应内容类型(Content-Type),并支持设置字符编码。以下是其核心作用和使用场景的详细说明:
一、主要作用
-
指定响应内容类型
produces
通过设置媒体类型(如application/json
、text/html
)明确告知客户端返回数据的格式。例如:@RequestMapping(value = "/data", produces = "application/json") @ResponseBody public User getUser() { /* 返回JSON数据 */ }
此时,响应头的
Content-Type
会被自动设置为application/json
,覆盖Spring Boot默认的text/html
。 -
设置字符编码
可以附加charset
参数解决中文乱码问题,例如:@RequestMapping(produces = "application/json;charset=utf-8")
这会强制响应的字符编码为UTF-8。
-
匹配客户端Accept请求头
当客户端请求中携带Accept
头(如Accept: application/json
)时,只有produces
指定的类型与Accept
匹配,才会触发该方法处理请求;否则返回406错误。
二、使用场景与最佳实践
-
明确API响应格式
在RESTful API开发中,显式声明produces
可避免因默认类型导致的解析问题。例如前端要求返回JSON时,即使方法返回值是字符串,也会被强制序列化为JSON。 -
多版本API支持
通过不同的produces
值区分同一接口的不同版本:@GetMapping(value = "/user", produces = "application/vnd.company.v1+json") public UserV1 getUserV1() { /* 版本1 */ } @GetMapping(value = "/user", produces = "application/vnd.company.v2+json") public UserV2 getUserV2() { /* 版本2 */ }
-
与
@ResponseBody
的协同使用
当类或方法上标注@ResponseBody
(或@RestController
)时,返回值会直接序列化为响应体。此时produces
可替代@ResponseBody
的部分功能,但更推荐两者结合以增强可读性。
三、与consumes
的对比
属性 | 作用方向 | 典型值示例 | 应用场景 |
---|---|---|---|
consumes | 请求内容类型 | application/json | 限制客户端请求的提交格式 |
produces | 响应内容类型 | text/html;charset=utf-8 | 控制服务器返回的数据格式 |
四、常见问题
-
优先级问题
produces
的优先级高于@ResponseBody
的默认行为。若两者冲突(如@ResponseBody
默认JSON,而produces
设为XML),以produces
为准。 -
编码不生效
需确保produces
中charset
与项目全局编码配置一致,避免因配置冲突导致乱码。
五、示例代码
// 返回UTF-8编码的JSON数据
@PostMapping(value = "/create", produces = "application/json;charset=utf-8")
public ResponseEntity<Result> createUser() {
return ResponseEntity.ok(Result.success());
}
// 仅处理Accept包含text/xml的请求
@GetMapping(value = "/info", produces = "text/xml")
public String getInfoAsXml() { /* 返回XML数据 */ }
通过合理使用produces
,开发者可以精确控制API的响应格式,提升接口的规范性和兼容性。更多细节可参考Spring官方文档或思否、CSDN等社区的技术解析。
对应的java类
// org.springframework.http.MediaType
static {
// Not using "valueOf' to avoid static init cost
ALL = new MediaType("*", "*");
APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
APPLICATION_CBOR = new MediaType("application", "cbor");
APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
APPLICATION_GRAPHQL = new MediaType("application", "graphql+json");
APPLICATION_JSON = new MediaType("application", "json");
APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
APPLICATION_NDJSON = new MediaType("application", "x-ndjson");
APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
APPLICATION_PDF = new MediaType("application", "pdf");
APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json");
APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json", StandardCharsets.UTF_8);
APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml");
APPLICATION_RSS_XML = new MediaType("application", "rss+xml");
APPLICATION_STREAM_JSON = new MediaType("application", "stream+json");
APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
APPLICATION_XML = new MediaType("application", "xml");
IMAGE_GIF = new MediaType("image", "gif");
IMAGE_JPEG = new MediaType("image", "jpeg");
IMAGE_PNG = new MediaType("image", "png");
MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
MULTIPART_MIXED = new MediaType("multipart", "mixed");
MULTIPART_RELATED = new MediaType("multipart", "related");
TEXT_EVENT_STREAM = new MediaType("text", "event-stream");
TEXT_HTML = new MediaType("text", "html");
TEXT_MARKDOWN = new MediaType("text", "markdown");
TEXT_PLAIN = new MediaType("text", "plain");
TEXT_XML = new MediaType("text", "xml");
}
Get请求的Content-type
需要注意的是,一般get请求不需要设置Content-Type,只有post才有必要设置!
为什么get请求不需要设置Content-Type?
那要从Content-Type的作用说起,Content-Type作用是为了告诉别人我携带的数据是什么格式?
对于request请求
get是不携带数据的,url中?后的参数不算做data
post是需要带参数的,也就是data参数,客户端告诉服务端,自己的数据类型
对于get请求,在url地址中携带的参数,在服务端,使用@RequestParam
来接收。
虽然,get请求不需要指定Content-Type,但是如果url地址中携带的参数,包含一些特殊字符或者中文等,此时就需要使用cn.hutool.core.net.url.UrlBuilder
来构建有效的url地址。
UrlBuilder previewUrlBuilder = UrlBuilder.of().setCharset(StandardCharsets.UTF_8).setScheme(directAccessProperties.getScheme())
.setHost(directAccessProperties.getHost()).setPort(directAccessProperties.getPort())
.addPath(FileUploadConstants.PREVIEW_URL_PREFIX)
.addQuery(FileUploadConstants.COMPANY_ID_STR, companyId).addQuery(FileUploadConstants.ID_STR, fileListQueryRespDTO.getId())
.addQuery(IotSecurityConstant.TOKEN_ID_PARAMETER, loginUserProvider.getLoginUser().getId());
Post请求,Client各Content-type与Server的接收参数的方式
常见的content-type,有以下几种:
- application/x-www-form-urlencoded
- application/json;charset=UTF-8
- multipart/form-data
针对上面的几种content-type,在server端,接收参数的方式,分别如下:
1.content-type是application/json;charset=UTF-8,在server端,使用@RequestBody
来接收数据
2.content-type是application/x-www-form-urlencoded,在server端,使用@RequestParam
来接收数据 或者 直接使用对象
来接收,不加任何注解
3.content-type是multipart/form-data,在server端,使用@RequestPart
来接收数据.
在处理复杂请求时,@RequestPart 注解是一个非常有用的工具。它允许我们同时处理文件上传和其他表单数据。
在使用 @RequestPart 时,可以指定参数名来提取请求中的文件和数据。例如,@RequestPart("file") 用于提取文件,@RequestPart("jsonData") 用于提取 JSON 数据。