数据传输
后端->前端
HttpMessageConverter有待后续继续学习
简单来说,@ResponseBody可以直接返回json结果,ResponseEntity不仅可以返回json结果,还可以定义返回的HttpHeaders和HttpStatus
_@_ResponseBody
@ResponseBody可以将java对象转为json格式的数据
@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
默认情况下,使用@ResponseBody返回的数据只能是String类型,其它类型返回时会出现异常:
若想转换为json格式,使用jackson即可,只需要引入jackson的依赖坐标,SpringMVC会自动添加Jackson的转换器
使用此注解后不会再走视图处理器,不进行页面跳转,直接将数据写入到输入流中
@ResponseBody通常用于web层,而web层在Spring项目当中还有另外一个注解,用于声明web层,即为@Controller,因此提供了一个新的注解@RestController,用于整合二者,因此,以后要进行数据返回时只需要加上一个@RestController即可,也就是说它既可以用于声明类为Controller,也可以给所有方法自动添加@ResponseBody注解。
以下是开发中的实际使用案例:
/*对单篇文章进行CRUD*/
@RestController
@RequestMapping(path = "/articles/{slug}")
public class ArticleApi {
private ArticleQueryService articleQueryService;
private ArticleRepository articleRepository;
private ArticleCommandService articleCommandService;
private UserDao userDao;
@Autowired
public ArticleApi(
ArticleQueryService articleQueryService,
ArticleRepository articleRepository,
ArticleCommandService articleCommandService,
UserDao userDao) {
this.articleQueryService=articleQueryService;
this.articleCommandService=articleCommandService;
this.articleRepository=articleRepository;
this.userDao=userDao;
}
@GetMapping
public ResponseEntity<?>article(
@PathVariable("slug") String slug/*, User user*/){
/*假User*/
User user=userDao.selectById("1234");
Optional<ArticleData> articleData = articleQueryService.findBySlug(slug, user);
return articleData
.map(articleData1 -> ResponseEntity.ok(articleResponse(articleData1)))
.orElseThrow(ResourceNotFoundException::new);
}
}
ResponseEntity
ResponseEntity继承了HttpEntity,可以添加HttpStatus状态码的HttpEntity的扩展类。被用于RestTemplate和Controller层方法
ReponseEntity的最大作用为将响应数据的toString字符串转换为json格式,即当返回形式为ResponseEntity时,@ResponseBody可写可不写,或者不使用@RestController只使用@Controller即可
ResponseEntity标识整个http相应:状态码、头部信息以及相应体内容。因此我们可以使用其对http响应实现完整配置。由于可以设置状态码,因此其作用相当于==@ResponseStatus与@ResponseBody结合起来==
使用方式
构造方法
public class ResponseEntity<T> extends HttpEntity<T> {
public ResponseEntity(HttpStatus status) {
this((Object)null, (MultiValueMap)null, (HttpStatus)status);
}
public ResponseEntity(@Nullable T body, HttpStatus status) {
this(body, (MultiValueMap)null, (HttpStatus)status);
}
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status) {
this((Object)null, headers, (HttpStatus)status);
}
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.status = status;
}
private ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, Object status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.status = status;
}
}
可以看到,ResponseEntity类提供的构造方法,状态码为必传的,header和响应体为可选的,使用时我们只需要new一个ResponseEntity对象作为API的返回值即可
需要注意的是,在有headers作为参数的构造方法中,需要传入一个类型为MultiValueMap的参数。MultiValueMap继承自Map这个抽象类,其中拥有一个叫做HttpHeaders的子类,我们可以当它为一个key和value都为String类型的HashMap使用。HttpHeaders里面保存了一些常用的Header的key值,例如”Accept-Charset”。当然也可以自定义一些特殊的key。 HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin");
return new ResponseEntity<>(resultBody, headers, HttpStatus.OK);
以下为具体使用案例:
如果需要使用ResponseEntity,必须在请求点返回,通常在spring rest中实现。ResponseEntity是通用类型,因此可以使用任意类型作为响应体:@GetMapping("/hello")
ResponseEntity<String> hello() {
return new ResponseEntity<>("Hello World!", HttpStatus.OK);
}
可以通过指明响应状态,所以根据不同场景返回不同状态:
@GetMapping("/age")
ResponseEntity<String> age(
@RequestParam("yearOfBirth") int yearOfBirth) {
if (isInFuture(yearOfBirth)) {
return new ResponseEntity<>(
"Year of birth cannot be in the future",
HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(
"Your age is " + calculateAge(yearOfBirth),
HttpStatus.OK);
}
设置响应头:
@GetMapping("/customHeader")
ResponseEntity<String> customHeader() {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "foo");
return new ResponseEntity<>(
"Custom header set", headers, HttpStatus.OK);
}
header部分暂时用到的还不多,需要继续学习
ResponseEntity.BodyBuilder
public static ResponseEntity.BodyBuilder status(HttpStatus status) {
Assert.notNull(status, "HttpStatus must not be null");
return new ResponseEntity.DefaultBuilder(status);
}
public static ResponseEntity.BodyBuilder status(int status) {
return new ResponseEntity.DefaultBuilder(status);
}
public static ResponseEntity.BodyBuilder ok() {
return status(HttpStatus.OK);
}
public static ResponseEntity.BodyBuilder created(URI location) {
ResponseEntity.BodyBuilder builder = status(HttpStatus.CREATED);
return (ResponseEntity.BodyBuilder)builder.location(location);
}
public static ResponseEntity.BodyBuilder accepted() {
return status(HttpStatus.ACCEPTED);
}
public static ResponseEntity.HeadersBuilder<?> noContent() {
return status(HttpStatus.NO_CONTENT);
}
public static ResponseEntity.BodyBuilder badRequest() {
return status(HttpStatus.BAD_REQUEST);
}
public static ResponseEntity.HeadersBuilder<?> notFound() {
return status(HttpStatus.NOT_FOUND);
}
public static ResponseEntity.BodyBuilder unprocessableEntity() {
return status(HttpStatus.UNPROCESSABLE_ENTITY);
}
.........
ResponseEntity提供了两个内嵌的构建器接口: HeadersBuilder 和其子接口 BodyBuilder。因此我们能通过ResponseEntity的静态方法直接访问。
最简单的情况是相应包括一个主体及http 200响应码:
@GetMapping("/hello")
ResponseEntity<String> hello() {
return ResponseEntity.ok("Hello World!");
}
大多数常用的http 响应码,可以通过下面static方法设置,也可使用HttpStatus枚举类手动设置
BodyBuilder accepted();
BodyBuilder badRequest();
BodyBuilder created(java.net.URI location);
HeadersBuilder<?> noContent();
HeadersBuilder<?> notFound();
BodyBuilder ok();
另外,可以能使用BodyBuilder status(HttpStatus status)和BodyBuilder status(int status) 方法设置http状态。使用ResponseEntity BodyBuilder.body(T body)设置http响应体:
@GetMapping("/age")
ResponseEntity<String> age(@RequestParam("yearOfBirth") int yearOfBirth) {
if (isInFuture(yearOfBirth)) {
return ResponseEntity.badRequest()
.body("Year of birth cannot be in the future");
}
return ResponseEntity.status(HttpStatus.OK)
.body("Your age is " + calculateAge(yearOfBirth));
}
替代方法
- _@_ReponseBody
上述已经详细介绍,不再说明 _@_ResponseStatus
当请求点成功返回,spring提供http 200(ok)相应。如果请求点抛出异常,spring查找异常处理器,由其返回相应的http状态码。对这些方法增加@ResponseStatus注解,spring会返回自定义http状态码。
例:@ResponseStatus(HttpStatus.FORBIDDEN)
public class NoAuthorizationException extends RuntimeException {
}
异常处理类,抛出该自定义异常时,返回自定义的HTTP Status.FORBIDEN(403)响应码
实际开发中的应用:
查询后如果未查询到,则抛出自定义的资源未找到异常,通过@ResponseStatus定义了响应码为404
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
}
若授权未通过,则抛出自定义的无权限异常处理类,响应码为403
@ResponseStatus(HttpStatus.FORBIDDEN)
public class NoAuthorizationException extends RuntimeException {
}
若无异常则,通过ResponseEntity.ok封装响应体和响应码200OK
@PutMapping
public ResponseEntity<?> updateArticle(
@PathVariable("slug") String slug,
@AuthenticationPrincipal User user,
@Valid @RequestBody UpdateArticleParam updateArticleParam) {
return articleRepository
.findBySlug(slug)
.map(
article -> {
if (!AuthorizationService.canWriteArticle(user, article)) {
throw new NoAuthorizationException();
}
Article updatedArticle = articleCommandService.updateArticle(article, updateArticleParam);
return ResponseEntity.ok(
articleResponse(articleQueryService.findBySlug(updatedArticle.getSlug(), user).get()));
})
.orElseThrow(ResourceNotFoundException::new);
}
realworld项目之中的例子遇到再进行补充
@GetMapping("/age")
ResponseEntity<String> age(
@RequestParam("yearOfBirth") int yearOfBirth) {
if (isInFuture(yearOfBirth)) {
return new ResponseEntity<>(
"Year of birth cannot be in the future",
HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(
"Your age is " + calculateAge(yearOfBirth),
HttpStatus.OK);
}
前端->后端
常见的接口有如下四种类型,分别是含有查询参数的接口,表单类型的接口,json类型的接口以及含有上传文件的接口。
- 用来处理Content-Type为:application/x-www-form-urlencoded编码的内容。(Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型)
- RequestParam用于处理query参数时,实质是将Request.getParameter()中的Key-value参数Map利用Spring的转化机制ConversionService配置,转化成参数接收对象或字段。
- 在Content-Type: application/x-www-form-urlencoded的请求中, get 方式中queryString的值,和post方式中 body data的值都会被Servlet接受到并转化到Request.getParameter()参数集中,所以@RequestParam可以获取的到。
实际应用:
@GetMapping
public ResponseEntity getArticles(
@RequestParam(value = "offset",defaultValue = "0") int offset,
@RequestParam(value = "limit",defaultValue = "20") int limit,
@RequestParam(value = "tag",required = false) String tag,
@RequestParam(value = "favorited",required = false) String favoritedBy,
@RequestParam(value = "author",required = false) String author
/*User user*/) {
/*假User*/
User user=userDao.selectById("1234");
ArticleDataList recentArticles = articleQueryService.findRecentArticles(
tag,
author,
favoritedBy,
new MyPage(offset, limit),
user);
return ResponseEntity.ok(recentArticles);
}
@RequestParam用于形参上
- value为request.getParameter()中的参数,表示把以value为key的值赋给该形参
- required表示该参数是否为必须,true为必须,默认情况下为true,在false的情况下,如果不传入则按null处理,不影响service层,dao层的工作(前提是已经对空值进行了处理)
- defaultValue:默认值
@RequestBody(可使用多次,但建议只使用一次)
- 处理HttpEntity传递过来的数据,一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据。
- GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。
如:
数据形式为:Content-Type:application/json
X-Requested-With:XMLHttpRequest
Authorization:Token {{token}}
@RequestBody注解一次性将请求体中的数据全部取出来,所以不建议在一次方法中使用多次
以下为项目中的实际使用案例:
@PostMapping
public ResponseEntity createArticle(
@Valid @RequestBody NewArticleParam newArticleParam/*, User user*/){
/*假User*/
User user=userDao.selectById("1234");
Article article = articleCommandService.createArticle(newArticleParam, user);
return ResponseEntity.ok(
new HashMap<String,Object>() {
{
put("article",articleQueryService.findById(article.getId(),user).get());
}
});
};
通过@RequestBody从请求体中获取全部数据赋给NewArticleParam,用于进行文章的创建,后台会根据Content-Type然后结合接收到的内容,进行json的反序列化,将json变成对象
请求体中的数据为json格式,如下:
{"article":{
"title":"How to train your dragon",
"description":"Ever wonder how?",
"body":"Very carefully.",
"tagList":["training", "dragons"]
}
}
json字符串中,如果value为””的话,后端对应属性如果是String类型的,那么接受到的就是””,如果是后端属性的类型是Integer、Double等类型,那么接收到的就是null。
json字符串中,如果value为null的话,后端对应收到的就是null。
如果某个参数没有value的话,在传json字符串给后端时,要么干脆就不把该字段写到json字符串中;要么写value时, 必须有值,null 或””都行。
@PathVariable(一个方法中可以使用多次)
需要配合rest风格url使用,目的是接收rest Url中的参数,可以拼接多个
例如: 请求路径[http://127.0.0.1:8082/hello/4/zs](http://127.0.0.1:8082/hello/4/zs)
@RequestMapping(value = "/hello/{pageSize}/{name}", method = RequestMethod.GET)
public String say(@PathVariable("pageSize") String pageSize,@PathVariable("name") String name) {
return pageSize;
}
实际开发中的应用:从url中获取参数赋给slug
@RequestMapping(path = "/articles/{slug}")
public class ArticleApi {
@GetMapping
public ResponseEntity<?>article(
@PathVariable("slug") String slug/*, User user*/){
/*假User*/
User user=userDao.selectById("1234");
Optional<ArticleData> articleData = articleQueryService.findBySlug(slug, user);
return articleData
.map(articleData1 -> ResponseEntity.ok(articleResponse(articleData1)))
.orElseThrow(ResourceNotFoundException::new);
}
}
@RequestPart(主要用来接收文件)
有待后续继续学习
- @RequestPart这个注解用在multipart/form-data表单提交请求的方法上。
- 支持的请求方法的方式MultipartFile,属于Spring的MultipartResolver类。这个请求是通过http协议传输的。
- @RequestParam也同样支持multipart/form-data请求。(即两者都能用于后端接收文件)
- 他们最大的不同是,当请求方法的请求参数类型不再是String类型的时候。
@RequestParam适用于name-valueString类型的请求域,@RequestPart适用于复杂的请求域(像JSON,XML)。
总结
在GET请求中,不能使用@RequestBody。
- 在POST请求中,可以使用@RequestBody和@RequestParam,但是如果使用@RequestBody,对于参数转化的配置必须统一。
- 如果使用@RequestParam来接受参数,可以在接受参数的model中设置@DateFormat指定所需要接受时间参数的格式。
- 另外,使用@RequestBody接受的参数是不会被Servlet转化统一放在request对象的Param参数集中,@RequestParam是可以的。
- 综上所述,一般情况下,推荐使用@RequestParam注解来接受Http请求参数。
Get请求不能使用表单(没有请求体),只能在url中传参,传参方式只有这一种。 Post请求可以使用表单,也可以在url中传参。
实际使用情况
如果项目组规定使用 rest 风格的api 并且参数不是很复杂不是很多的得情况下优先使用 _@_PathVariable 注解接收路径穿过来的参数
- 能使用_@_PathVariable 注解的地方都能使用 _@_RequestParam 进行替换 ,_@_RequestParam 注解既能接收get请求 问号传参过来的参数,也能接收post请求 问号传参,(post也能使用问号传参)以及post请求 并且Content-Type: 为 application/x-www-form-urlencoded 通常用于接收文件
- _@_RequestPart 注解可以用来替换 _@_RequestParam 接收文件以及其他更为复杂的数据类型(json xml等等)
- _@_RequestBody 多用于接收post请求 中的请求体的内容,(json数据,大多对应后端的一个实体,或Map类型的数据 等等)
- _@_PathVariable _@_RequestParam _@_RequestBody _@_RequestPart 这四个注解能混合使用,并且每一次注解都支持使用 (required = false)
参考:
- @RequestBody、@RequestParam 、@PathVariable @RequestPart 傻傻分不清 - 掘金 (juejin.cn)
- 常见的接口请求类型和@RequestBody、@RequestParam的使用 - 掘金 (juejin.cn)
- (68条消息) 使用spring ResponseEntity处理http响应_neweastsun的专栏-CSDN博客_responseentity
- (68条消息) 使用ResponseEntity处理API返回_学习即修行-CSDN博客_responseentity 返回
- https://blog.csdn.net/qq_39038793/article/details/104647434