《Spring Boot 实战派》--09.接口架构风格--Restful
第9章 接口架构风格---Restful
本章首先介绍REST的特征、HTTP方法与CRUD动作映射;
然后讲解如何基于Spring Boot为PC、手机APP构建统一风格的Restful API ;
最后讲 解在 Spring BootT如何使用 RestTempiate 发送 GET、POST. DELETE、PUT 等请求。
9.1 RES--前后台间的通信方式
9.1.1 认识 REST
1、什么是REST
REST是软件架构的规范体系结构,它将资源的状态以适合客户端的形式从服务器端发送到客户端或相反方向)。
在REST中,通过URL进行资源定位,用HTTP动作(GET、POST、DELETE、 PUSH等)描述操作,完成功能。
2、REST的特征
- 客户一服务器(client-server): 提供服务的服务器和使用服务的客户端需要被隔离对待。
- 无状态(stateless): 服务器端不存储客户的请求中的信息,客户的每一个请求必须包含服 务器处理该请求所需的所有信息,所有的资源都可以通过URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而变化。
Restful是典型的基于HTTP的协议。HTTP连接最显著的特点是:客户端发送的每次请求都需要服务器回送响应;在请求结束后,主动释放连接。
从建立连接到关闭连接的过程称为“一次连接”,前后的请求没有必然的联系,所以是无状态的。
- 可缓存(cachable ):服务器必须让客户知道请求是否可以被缓存。
- 分层系统(layered System ):服务器和客户之间的通信必须被标准化。
- 统一接口( uniform interface):客户和服务器之间通信的方法必须统一,REStful风格的 数据元操作 CRUD ( create、read、update、delete )分别对应 HTTP 方法:
GET 用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口。
- HTTP状态码:状态码在REST中都有特定的意义:200、201、202、204、400、401、 403、500。比如,401表示用户身份认证失败;403表示验证身份通过了,但资源没有权限进行操作。
- 支持按需代码(Code-On-Demand,可选):服务器可以提供一些代码或脚本,并在客户的运行环境中执行。
9.1.2认识HTTP方法与CRUD动作映射
RESTful风格使用同一个URL,通过约定不同的HTTP方法来实施不同的业务。
普通网页的CRUD和RESTful风格的CRUD的区别,见表:
动作 |
普通CRUD的URL |
普通CRUD的HTTP方法 |
Restful 的 URL |
Restful 的 CRUD 的 HTTP 方法 |
查询 |
Article/id=1 |
GET |
Article/(id} |
GET |
添加 |
Article?title=xxx&body=xxx |
GET/POST |
Article |
POST |
修改 |
Article/update?id=xxx |
GET |
Article/(id} |
PUT 或 PATCH |
删除 |
Article/delete?id=xxx |
GET |
Article/(id) |
DELETE |
9.1.3实现RESTful风格的数据增加、删除、修改和查询
在Spring Boot中,如果要返回JSON数据,贝U只需要在控制器中用@RestController注解。 如果提交HTTP方法,则使用注解@RequestMapping来实现,它有以下两个属性
- Value:用来制定URI。
- Method:用来制定HTTP请求方法
(1 )获取列表采用的是GET方式,返回List。例如,下面代码返回Article的List。
@RequestMapping(value = "/", method = RequestMethod.GET) public List<Article> getArticleList() ( List<Article> list = new ArrayList<Article>(articleRepository.findAll()); return list; }
(2)增加内容(提交内容)采用的是POST方式,一般返回String类型或int类型的数据,见 以下代码:
@RequestMapping(value = "/", method = RequestMethod.POST) public String add(Article article) { articleRepository.save(article); return "success"; )
(3 )删除内容,必须采用DEIETE方法。一般都是根据id主键进行删除的。
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public String delete(@PathVariable("id") long id) ( articleRepository.deleteByld(id); return "success"; }
(4 )修改内容,贝味用PUT方法。
@RequestMapping(value = "/{id}", method = RequestMethod.PUT) public String update(Article model) { articleRepository.save(model); return "success"; }
(5)查询内容,和上面获取列表的方法一样,也是采用GET方法。
@RequestMapping(value = "/{id}", method = RequestMethod.GET) public Article findArticle(@PathVariable("id") Integer id) ( Article article = articleRepository.findByld(id); return article;
9.2 设计统一的Restful风格的数据接口
9.2.1版本控制
REST不提供版本控制指南,常用的方法可以分为3种:
1、通过URL
二级目录的方式:
- API 版本 V1 : http://eg.com/api/v1
- API 版本 V2: http://eg.com/api/v2
二级域名的方式:
- API 版本 V1: http://v1.eg.como
- API 版本 V2: http://v2.eg.como
还可以包括日期、项目名称或其他标识符。这些标识符对于开发API的团队来说足够有意义, 并且随着版本的变化也足够灵活。
2、通过自定义请求头
自定义头(例如,Accept-version )允许在版本之间保留URL。
3、通过Accept标头
客户端在请求资源之前,必须要指定特定头,然后API接口负责确定要发送哪个版本的资源。
9.2.2过滤信息
如果记录数量很多,贝U服务器不可能一次都将它们返回给用户。API应该提供参数,实现分页 返回结果。下面是一些常用的参数。
- ?limit=10:指定返回记录的数量。
- ?page=5&size=10:指定第几页,以及每页的记录数。
- ?search_type=1:指定筛选条件。
9.2.3确定HTTP的方法
在RESTful中,HTTP的方法有以下几种。
- GET:代表请求资源。
- POST:代表添加资源。
- PUT:代表修改资源。PUT是进行全部的修改,大家在编写修改功能时可能会遇到这样的 情况:只修改了一个字段,但提交之后导致其他字段为空。
这是因为,其他字段的值没有一 起提交,数据库默认为空值。如果只修改一个或几个字段,则可以使用PATCH方法。
- DELETE:代表删除资源。
- HEAD:代表发送HTTP头消息,GET中其实也带了 HTTP头消息。
- PATCH: PUT PATCH 75;去比较相似,但它们的用法却完全不同,PUT用于替换资源, 而PATCH用于更新部分资源。
- OPTIONS:用于获取URI所支持的方法。返回的响应消息会在HTTP头中包含11 Allow" 的信息,其值是所支持的方法,如GET。
9.2.4确定HTTP的返回状态
HTTP的返回状态一般有以下几种。
- 200:成功。
- 400:错误请求。
- 404:没找到资源。
- 403:禁止。
- 406:不能使用请求内容特性来响应请求资源,比如请求的是HTML文件,但是消费者的 HTTP头包含了 JSON要求。
- 500:服务器内部错误。
9.2.5定义统一返回的格式
为了保障前后端的数据交互的顺畅,建议规范数据的返回,并采用固定的数据格式封装。如:
异常信息:
{"code":10001, "msg":"异常信息", "data":null}
成功信息:
{ "code":200, "msg":"成功", "data":{ "name":"longzhiran", "age":2 } }
9.3 实例32:为手机APP、PCs H5网页提供统一风格的API
9.3.1实现响应的枚举类
枚举是一种特殊的数据类型,它是一种“类类型”,比类型多了一些特殊的约束。创建枚举类型 要使用“enum”,表示所创建的类型都是java.lang.Enum类(抽象类)的子类。见以下代码:
//实现响应的枚举类 public enum ExceptionMsg {
SUCCESS("200", ”操作成功"), FAILED(”999999”,”操作失败");
private String code;
private String msg;
private ExceptionMsg(String code, String msg) {
this.code = code; this.msg = msg; }
}
9.3.2实现返回的对象实体
实现返回的对象实体,返回Code和Message (信息),见以下代码:
//实现返回对象实体 public class Response { /**返回信息码*/ private String rspCode= "200"; /**返回信息内容*/ private String rspMsg="操作成功"; //省略 }
9.3.3封装返回结果
这里把返回的结果进行封装,以显示数据,见以下代码:
package com.example.demo.result
//封装返回结果 public class ResponseData extends Response {
private Object data; public ResponseData(Object data) ( this.data = data; } //省略 }
9.3.4统一处理异常
自定义全局捕捉异常,见以下代码:
package com.feng.springbootmybatis; @RestControllerAdvice public class GlobalExceptionHandler { //日志记录工具 private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MissingServletRequestParameterException.class) public Map<String, Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { logger.error("缺少请求参数", e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 400); map.put("message", e.getMessage()); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public Map<String, Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e){ logger.error("缺少请求参数", e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 400); map.put("message", e.getMessage()); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) ( logger.error("缺少请求参数", e); BindingResult result = e.getBindingResult(); FieldError error = result.getFieldError(); String field = error.getField(); String code = error.getDefaultMessage(); String message = String.format("%s:%s", field, code); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 400); map.put("message", message); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(BindException.class) public Map<String, Object> handleBindException(BindException e) { logger.error("缺少请求参数", e); Map<String, Object> map = new HashMap<String, Object>(); BindingResult result 二 e.getBindingResult(); FieldError error = result.getFieldError(); String field = error.getField(); String code = error.getDefaultMessage(); String message = String.format("%s:%s", field, code); map.put("code", 400); map.put("message",message); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(ConstraintViolationException.class) public Map<String, Object> handleServiceException(ConstraintViolationException e) ( logger.error("少请求参数",e); Set<ConstraintViolation> violations = e.getConstraintViolations(); ConstraintViolation<?> violation = violations.iterator().next(); String message = violation.getMessage(); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 400); map.put("message", message); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(ValidationException.class) public Map<String, Object> handleValidationException(ValidationException e) { logger.error("数验证失败",e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 400); map.put("message", e.getMessage()); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 405 - Method Not Allowed */ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public Map<String, Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { logger.error("不支持当前请求方法”,e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 400); map.put("message", e.getMessage()); //如果发生异常,则进行日志记录,写入数据库或其他处理,此处省略 return map; } /** * 415 - Unsupported Media Type */ @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public Map<String, Object> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { logger.error("不支持当前媒体类型”,e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 415); map.put("message", e.getMessage()); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 自定义异常类 */ @ResponseBody @ExceptionHandler(BusinessException.class) public Map<String, Object> businessExceptionHandler(BusinessException e) { logger.error("自定义业务失败”,e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", e.getCode()); map.put("message", e.getMessage()); //如果发生异常,则进行日志记录、写入数据库或其他处理,此处省略 return map; } /** * 获取其他异常,包括500 */ @ExceptionHandler(value = Exception.class) public Map<String, Object> defaultErrorHandler(Exception e) { logger.error("自定义业务失败",e); Map<String, Object> map = new HashMap<String, Object>(); map.put("code", 500); map.put("message", e.getMessage()); //发生异常进行日志记录,写入数据库或其他处理,此处省略 return map; } }
9.3.5编写测试控制器
编写测试控制器来检验自定义业务,见以下代码:
package com.example.demo.controller; @RestController public class Testcontroller {
@RequestMapping("/BusinessException") public String testResponseStatusExceptionResolver(@RequestParam("i") int i){ if (i==0){ throw new BusinessException(600,"自定义业务错误"); } throw new ValidationException(); } }
运行项目,访问 "http://localhost:8080/BusinessException?i=1", 在网页中返回如下 JSON 格式的数据:
{"code":400,"message":null}
9.3.6实现数据的增加、删除、修改和查询控制器
实现数据的増加、删除、修改和查询控制器,并实现数据的返回,见以下代码:
package com.example.demo.controller; @RestController @RequestMapping("article") public class Articlecontroller {
protected Response result(ExceptionMsg msg){ return new Response(msg); }
protected Response result(){ return new Response(); }
@Autowired private ArticleRepository articleRepository;
@RequestMapping(value = "/",method = RequestMethod.GET) public ResponseData getArticleList() { List<Article> list = new ArrayList<Article>(articleRepository.findAll()); return new ResponseData(ExceptionMsg.SUCCESS, list); }
//增 @RequestMapping(value = "/", method = RequestMethod.POST) public ResponseData add(Article article) ( articleRepository.save(article); return new ResponseData(ExceptionMsg.SUCCESS,article); }
〃删 @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public Response delete(@PathVariable("id") long id) { articleRepository.deleteByld(id); return result(ExceptionMsg.SUCCESS); //return new ResponseData(ExceptionMsg.SUCCESS,""); }
//改 @RequestMapping(value = "/{id}", method = RequestMethod.PUT) public ResponseData update(Article model) ( articleRepository.save(model); return new ResponseData(ExceptionMsg.SUCCESS,model); }
//查 @RequestMapping(value = "/{id}", method = RequestMethod.GET) public ResponseData findArticle(@PathVariable("id") Integer id) throws lOException { Article article = articleRepository.findByld(id); if (article != null) { return new ResponseData(ExceptionMsg.SUCCESS,article); ) return new ResponseData(ExceptionMsg.FAILED,article);} }
9.3.7测试数据
现在启动项目,进行如下测试。
(1) 添加数据。用 POST 方式访问 "http://localhost:8080/article",提交 Article 实体。返回 如下结果:
{ "rspCode": "200", "rspMsg":"操作成功", "data": { "id": 1, "title": "test", "body":"测试数据” } }
(2) 查询刚刚添加的数据。用GET方法访问"http://localhost:8080/article/1",会返回如下 结果:
("rspCode":"200","rspMsg":"操作成功","data":{"id":1,"title":"test","body":"测试数据"}}
(3)修改数据,用PUT方法访问"http://localhost:8080/article/1",返回如下结果:
( "rspCode": "200", "rspMsg": ”操作成功”, "data": { "id": 1, "title": "edit title", "body": "修改数据" } }
(4) 删除数据。用DELETE方法访问"http://localhost:8080/article/1",如果返回如下结果, 则代表删除成功。
{"rspCode": "200", "rspMsg": "操作成功"}
(5)测试访问不存在的数据。用GET方法访问uhttp://localhost:8080/article/0,,J则返回如 下结果:
{ "rspCode": "999999", "rspMsg":"操作失败”, "data": null
}
9.4 实例33:用Swagger实现接口文档
下面演示如何在Spring Boot中集成Swagger。
9.4.1 配置 Swagger
(1)添加Swagger依赖。
在pom.xml文件中加入Swagger2的依赖,见以下代码:
<!--Swagger 依赖--> <dependency> <groupld>io.springfox</groupld>
<artifactld>springfox-swagger2</artifactld>
<version>2.9.2</version> </dependency> <!--Swagger-UI 依赖--> <dependency> <groupld>io.springfox</groupld>
<artifactld>springfox-swagger-ui</artifactld>
<version>2.9.2</version> </dependency>
(2)创建Swagger配置类。
创建Swagger配置类,完成相关配置项,见以下代码:
/** Swagger配置类 在与Spring Boot集成时,放在与Application.java同级的目录下 通过注解©Configuration让Spring来加载该类配置,再通过注解@EnableSwagger2来启用Swagger2 */
©Configuration @EnableSwagger2 public class Swagger2 { /** *创建API应用 * apilnfoO增加API相关信息 *通过select。函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现 *本例采用指定扫描的包路径来定义指定要建立API的目录 */ @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apilnfo(apilnfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller")) .paths(PathSelectors.any()) .build(); }
/** * 创建该API的基本信息(这些基本信息会展现在文档页面中) * 访问地址:http://项目实际地址ZswaggeLui.html */
private Apilnfo apilnfo() { return new ApilnfoBuilder() .title("RESTful APIs") .description("RESTful Apis") .terrhsOfServiceUrl("http://localhost:8080/") .contact("long") .version("1.0") .build();
}
}
代码解释如下。
- @Configuration:让Spring来加载该类配置。
- @EnableSwagger2: 启用 Swagger2.createRestApi 函数创建 Docket 的 Bean
- apilnfo():用来展示该API的基本信息。
- select():返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现。
- apis(RequestHandlerSelectors.basePackage()):配置包扫描路径。Swagger 会扫描包 下所有Controller定义的API,并产生文档内容。如果不想产生API,则使用注解 @Apilgnore
9.4.2编写接口文档
在完成上述配置后,即生成了文档,但是这样生成的文档主要针对请求本身,而描述自动根据 方法等命名产生,对用户并不友好。所以,通常需要自己增加一些说明以丰富文档内容。可以通过 以下注解来增加说明。
- @Api:描述类/接口的主要用途。
- @ApiOperation:描述方法用途,给API增加说明。
- @ApilmplicitParam:描述方法的参数,给参数增加说明。
- @ApilmplicitParams:描述方法的参数(Multi-Params ),给参数增加说明。
- @Apilgnore:忽略某类/方法/参数的文档。
具体使用方法见以下代码:
@ApiOperation(value="删除文章",notes="根据URL的id来指定删除对象") @ApilmplicitParam(name = "id", value ="文章 ID", required = true, dataType = "Long")
@RequestMapping(value = "/{id)", method = RequestMethod.DELETE)
public String del(@PathVariable("id") long id) { articleRepository.deleteByld(id); return "success"; }
完成上述代码后,启动项目,访问“http:// localhost:8080/swagge-ui.html”就能看到所展 示的RESTful API的页面,可以通过单击具体的API测试请求,来查看代码中配置的信息,以及 参数的描述信息。
9.5 用RestTemplate发起请求
9.5.1 认识 RestTemplate
RestTemplate用于同步Client端的核心类,简化与HTTP服务的通信。在默认情况下, RestTemplate默认依赖JDK的HTTP连接工具。
也可以通过setRequestFactory属性切换到不 同的 HTTP 源,比如 Apache HttpComponents、Netty 和 OkHttp。
RestTemplate简化了提交表单数据的难度,并附带自动转换为JSON格式数据的功能。该类的入口主要是根据HTTP的6种方法制定的,见表:
HTTP方法 |
RestTemplate 方法 |
HTTP方法 |
RestTemplate 方法 |
DELETE |
delete |
POST |
postForLocation |
GET |
getForObject |
POST |
postForObject |
GET |
getForEntity |
PUT |
put |
HEAD |
headForHeaders |
any |
exchange |
OPTIONS |
optionsForAllow |
any |
execute |
此外,exchange和excute也可以使用上述方法。
RestTemplate 默认使用 HttpMessageConverter 将 HTTP 消息转换成 POJO,或从 POJO 转换成HTTP消息,默认情况下会注册主MIME类型的转换器,
但也可以通过setMessageConverters 注册其他类型的转换器
9.5.2 实例 34:用 RestTemplate 发送 GET 请求
在RestTemplate中发送GET请求,可以通过getForEntity和getForObject两种方式。
本实例的源代码可以在"/09/RestTemplateDemo”目录下找到。
下面具体实现用RestTemplate发送GET请求。
1.创建测试实体
创建用于测试的实体,见以下代码:
public class User {
private long id; private String name;
}
2.创建用于测试的API
创建用于测试的API,见以下代码:
@RestController public class Testcontroller {
@RequestMapping(value = "/getparameter", method = RequestMethod.GET)
public User getparameter(User user) { return user; }
@RequestMapping(value = "/getuser1", method = RequestMethod.GET) public User user1() { return new User(1, "zhonghua"); }
@RequestMapping(value = "/postuser", method = RequestMethod.POST) public User postUser(User user) {
System.out.println("name:" + user.getName()); System.out.println("id:" + user.getld()); return user; } }
3.使用 getForEntity 测试
(1 )返回String,不带参数,见以下代码:
@Test public void nparameters() { RestTemplate client= restTemplateBuilder.build(); ResponseEntity<String> responseEntity = client.getForEntity("http://localhost:8080/getuser1", String.class); System.out.println(responseEntity.getBody()); }
运行测试单元,控制台输出如下结果:
{"id":l,"name":"zhonghua"}
(2)返回String,带参数的例子。
在调用服务提供者提供的接口时,有时需要传递参数,有以下两种不同的方式。
①用一个数字做占位符。最后是一个可变长度的参数,用来替换前面的占位符。使用方法见以 下代码:
@Test public void withparameters1() { RestTemplate client = restTemplateBuilder.build(); ResponseEntity<String> responseEntity =
client.getForEntity("http://localhost:8080/getparameter?name={1}&id={2}", String.class, "hua",2); System.out.println(responseEntity.getBody()); }
运行测试单元,控制台输出如下结果:
("id":2,"name":"hua")
② 使用name={name}这种形式。最后一个参数是一个map, map的key即为前边占位符的 名字,map的value为参数值。使用方法见以下代码:
@Test public void withparameters2() { RestTemplate client = restTemplateBuilder.build(); Map<String, String> map = new HashMap<>();
map.put("name", "zhonghuaLong"); ResponseEntity<String> responseEntity =
client.getForEntity("http://localhost:8080/getparameter?name={name)&id=3", String.class, map); System.out.println(responseEntity.getBody()); }
运行测试单元,控制台输出如下结果:
{"id" :3,"name": "zhonghuaLong" }
(3) 返回对象,见以下代码:
@Test public void restUserl () {
RestTemplate client = restTemplateBuilder.build(); ResponseEntity<User> responseEntity = client.getForEntity("http://localhost:8080/getuser1", User.class); System.out.println(responseEntity.getBody().getld()); System.out.println(responseEntity.getBody().getName()); )
运行测试单元,控制台输出如下结果:
1 zhonghua
4、使用 getForObject
getForObject函数是对getForEntity函数的进一步封装。如果你只关注返回的消息体的内容, 对其他信息都不关注,则可以使用getForObject,见以下代码:
@Test public void getForObject() { RestTemplate client= restTemplateBuilder.build(); User user = client.getForObject("http://localhost:8080/getuser1", User.class);
System.out.println(user.getName()); }
运行测试单元,控制台输出如下结果:
zhonghua
9.5.3 实例 35:用 RestTemplate 发送 POST 请求
在 RestTemplate 中,POST 请求可以通过 postForEntity、postForObject、 postForLocation。
exchange四种方法来发起:
用 postForEntity、postForObject、postForLocation 三种方法传递参数时,
Map 不能被定义为HashMap、LinkedHashMap,而应被定义为LinkedMultiValueMap,这样参数才能成功传递至寸后台。
下面讲解它们的用法。
1、方法一:使用 postForEntity
postForEntity(String url,Object request,Class responseType,Object... urlVariables) postForEntity(String url,Object request,Class responseType,Map urlVariables) postForEntity(String url,Object request,Class responseType)
方法 一 的第1个参数表示要调用的服务的地址。第2个参数表示上传的参数。第3个参数表示返回的消息体的数据类型。
2、方法二:使用 postForObject
postForObject(String url,Object request,Class responseType,Object... urlVariables) postForObject(String url,Object request,Class responseType,Map urlVariables) postForObject(String url,Object request,Class responseType)
3、方法三:使用 postForLocation
postForLocation也用于提交资源。在提交成功之后,会返回新资源的URI。它的参数和前面两种方法的参数基本一致,
只不过该方法的返回值为URI,表示新资源的位置。
• postForLocation(String url,Object request,Object... urlVariables) • postForLocation(String url,Object request,Map urlVariables) • postForLocation(String url,Object request)
4、方法四:使用exchange
使用exchange方法可以指定调用方式,使用方法如下:
ResponseEntity<String> response= template.exchange(newUrl,HttpMethod.DELETE,request,String.class);
5、实现发送POST请求
(1 )使用 postForEntity
@Test public void postForEntity {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("name", "longzhiran"); paramMap.add("id", 4); ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://localhost:8080/postuser", paramMap, User.class); System.out.println( responseEntity.getBody().getName()); }
代码解释如下。
- MultiValueMap:封装参数,千万不要替换为Map与HashMap,否则参数无法被传递。
- restTemplate.postForEntity("url", paramMap, User.class):参数分别表示要调用的服务 的地址、上传的参数、返回的消息体的数据类型。
运行测试单元,控制台输出如下结果:
longzhiran
(2) 使用 postForObject
postForObject和getForObject相对应,只关注返回的消息体,见以下代码:
@Test public void postForObject() { //封装参数,干万不要替换为Map与HashMap,否则参数无法传递 MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("name", "longzhonghua"); paramMap.add("id", 4); RestTemplate client = restTemplateBuilder.build(); String response = client.postForObject(uhttp://localhost:8080/postuser", paramMap, String.class);
System.out.println(response); )
运行测试单元,控制台输出如下结果:
{"idn:4,"name":"longzhonghua"}
(3) 使用postForexchange,见以下代码:
@Test public void postForexchange() {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>(); paramMap.add("name", "longzhonghua"); paramMap.add("id", 4);
RestTemplate client = restTemplateBuilder.build(); HttpHeaders headers = new HttpHeaders(); //headers.set("id", "long"); HttpEntity<MultiValueMap<String, Object>> httpEntity =
new HttpEntity<MultiValueMap<String, Object>>(paramMap,headers); ResponseEntity<String> response =
client.exchange("http://localhost:8080/postuser", HttpMethod.POST,httpEntity,String.class,paramMap); System.out.println(response.getBody()); }
运行测试单元,控制台输出如下结果:
{"id":4,"name":"longzhonghua"}
(4) 使用 postForLocation
它用于提交数据,并获取返回的URI。一般登录、注册都是POST请求,操作完成之后,跳转 到某个页面,这种场景就可以使用postForLocation. 所以,先要添加处理登录的API,见以下 代码:
@RequestMapping(path ="success") public String loginSuccess(String name) { return "welcome" + name; }
@RequestMapping(path = "post", method = RequestMethod.POST) public String post(HttpServletRequest request,
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "password", required = false) String password, @RequestParam(value = "id", required = false) Integer id) {
return "redirect:/success?name=" + name + "&id=" + id + "&status=success"; }
然后使用postForLocation请求,用法见以下代码:
@Test public void postForLocation() {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>(); paramMap.add("name", "longzhonghua"); paramMap.add("id", 4); RestTemplate client = restTemplateBuilder.build(); URI response = client.postForLocation("http://localhost:8080/post", paramMap); System.out.println(response); }
运行测试单元,控制台输出如下结果:
redirect:/success?name=longzhonghua&id=4&status=success
如果有中文,则结果可能会出现乱码,可以用URLEncoder.encode(name, "UTF-S")进 行处理。
如果获取的值为"null”,则需要把URI添加到response信息的header中。
添加方法 为:
"response.add("Location",uri)"
9.5.4 用 RestTemplate 发送 PUT 和 DELETE 请求
本实例的源代码可以在“/09/RestTemplateDemo”目录下找到。
1. PUT请求
在RestTemplate中,发送“修改”请求和前面介绍的postForEntity方:去的参数基本一致, 只是修改请求没有返回值,用法如下:
@Test public void put() { RestTemplate client= restTemplateBuilder.build(); User user = new User();
user.setName("longzhiran");
client.put("http://localhost:8080/{1}", user, 4); }
最后的“4”用来替换前面的占位符{1}。
2. DELETE 请求
删除请求,可以通过调用DELETE方法来实现,用法见以下代码:
@Test public void delete() { RestTemplate client = restTemplateBuilder.build();
client.delete("http://localhost:8080/{1}"); }
最后的“4”用来替换前面的占位符{1}