互联网应用主流框架整合之构建REST风格的系统

REST(Representational State Transfer),中文译为“表述性状态转移”,是由Roy Fielding博士在他的博士论文中提出的一种软件架构风格,特别适用于网络应用的设计。REST不是一个标准,而是一种设计原则和约束集,它基于HTTP协议,以资源为中心,通过统一的接口和标准的方法来访问和操作这些资源。以下是REST风格的几个核心概念和原则:

核心概念

  • 资源(Resources):在REST中,网络上的所有内容都可以被视为资源。资源可以是文本、图片、视频、服务等任何可以命名的东西。每个资源都有一个唯一的标识符,即URI(Uniform Resource Identifier)
  • 表现层(Representations):资源的表现形式,即资源的具体数据表现,如HTML、XML、JSON等。客户端通过HTTP请求获取资源时,服务器返回的是资源的一个表现层,而不是资源本身
  • 状态转换(State Transfer):客户端和服务器之间通过HTTP协议进行交互,从而改变客户端的状态。这里的“状态转移”不是指服务器端的状态,而是指通过HTTP请求响应的方式,让客户端从一个状态转移到另一个状态

设计原则

  • 客户端-服务器(Client-Server):保持客户端和服务器职责的分离,使得它们可以独立地进化。客户端负责展示,服务器负责数据的管理和业务逻辑
  • 无状态(Stateless):服务器不保存客户端的会话信息。每次请求都必须包含理解该请求所必需的所有信息。这提高了系统的可伸缩性,因为服务器不需要为每个用户的会话维护状态
  • 可缓存(Cacheable):利用HTTP协议的缓存机制,使得响应可以在客户端、代理服务器等多级进行缓存,减少网络请求,提高响应速度
  • 分层系统(Layered System):系统可以设计成多层结构,每一层只与相邻层通信,这样可以简化复杂度,并且允许更容易地添加、修改或移除中间层,而不会影响整体架构
  • 统一接口(Uniform Interface):所有资源都通过统一的接口进行访问,主要通过HTTP标准方法(GET, POST, PUT, DELETE等)来实现对资源的增删查改操作
  • 按需代码(Code-On-Demand,可选):服务器可以提供可执行代码(如JavaScript),客户端可以选择执行这段代码来实现更丰富的功能。但这不是REST定义中的强制要求

REST风格的应用设计强调简单、灵活和高效,广泛应用于现代Web服务和API设计中,特别是对于需要跨平台、跨语言交互的场景,RESTful API因其规范性和易用性而成为首选。

最佳实践

RESTful风格则是遵循REST原则设计的Web服务。简单来说,当一个Web服务的设计完全符合REST的约束条件和原则时,我们称这个Web服务为RESTful,总体来说,REST是一种架构设计风格,而RESTful是这种风格的具体实践

RESTful API(Representational State Transferful Application Programming Interface)是一种遵循REST架构风格设计的Web服务API。它利用HTTP协议的特性,提供一套统一、简洁、无状态的接口设计模式,用于在客户端和服务器之间交换数据和管理资源。RESTful API的核心在于如何组织和访问网络上的资源,以及如何表述这些资源的状态。以下是RESTful API的一些关键特征和最佳实践:

关键特征

  • 资源导向(Resource-Oriented):RESTful API围绕资源展开,每个资源通过唯一的URL(Uniform Resource Locator)来标识,资源的URL应该清晰、直观,反映资源的层次关系
  • 标准HTTP方法(Standard HTTP Methods):利用HTTP协议预定义的方法来对资源进行操作,常见的有:
    • GET:从服务器检索资源(应该是安全和幂等的)
    • POST:向服务器提交数据,常用于创建新资源
    • PUT:替换服务器上的现有资源或创建指定资源(如果不存在)
    • PATCH:部分更新已有资源
    • DELETE:删除指定资源
  • 表述层多样性(Diverse Representations):支持多种数据格式(如JSON/XML/YAML等)来表示资源,客户端可以通过Accept头部指定期望的响应格式
  • 无状态(Statelessness):服务器不存储关于客户端的上下文信息,每次请求都包含完成该请求所需的所有信息,这有利于扩展性和负载均衡
  • 可缓存性(Cachability):利用HTTP的缓存机制,可以对响应进行缓存,减少网络请求,提高效率

最佳实践

  • 版本控制:在API的URL中加入版本号,以便于不同版本间的兼容和迁移,如/api/v1/users
  • 使用复数名词:资源的URL推荐使用复数形式,如使用/users而非/user,以更好地表达资源集合的概念
  • 过滤、排序和分页:提供查询参数来支持资源列表的过滤、排序和分页,如/users?state=active&sort=name&limit=10
  • 错误处理:使用合适的HTTP状态码来表示错误,如404表示资源未找到,同时返回易于理解的错误消息
  • HATEOAS(Hypermedia as the Engine of Application State):虽然在实际应用中较少严格遵循,但理想上,响应中应包含链接,指示客户端下一步可能的动作或相关资源,促进API的自发现性

RESTful API的设计旨在简化客户端与服务端之间的交互,提高系统的可扩展性和可维护性。通过遵循上述原则和最佳实践,可以构建出既强大又易于使用的Web服务接口

一些简单的例子如下所示:

#获取角色信息,1是角色编号
GET /role/1#查询多个角色
GET /roles/{roleName}#新建角色
POST /role/{roleName}/{note}#修改角色
PUT /role/{id}/{roleName}/{note}#使用动词,在REST风格设计中URI不该存在动词
GET /role/get/{id}#按版本获取角色
#这里请注意,当无论何种版本都指向同一个角色时,不建议将版本参数{version}放在URI中
#因为在REST风格中,一个URI就代表一个资源,不同的URI不该指向同一个资源
#可以考虑放在请求头中,这样URI依旧是GET role/{id}, 在请求头中放入版本参数即可
GET /role/{version}/{id}#错误使用HTTP参数,这里问号加id参数是不符合REST风格的
PUT /role/{roleName}?id=1
#可以修改为
PUT role/{id}/{roleName}

@ResponseBody

之前文章的代码中,使用MappingJackson2JsonView将结果转化为JSON视图,还有更简单的方法,就是使用注解@ResponseBody,只是它的原理和视图不同,功能上主要用于标注控制器的映射方法,将方法返回的结果转变为JSON数据集展示,示例代码如下

package com.springrest.controller;import com.springrest.pojo.Role;
import com.springrest.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/*** RoleController类负责处理与角色相关的HTTP请求。* 它使用RoleService来执行具体的业务逻辑。*/
@Controller
@RequestMapping("/role")
public class RoleController {/*** 自动注入RoleService实例,用于处理角色相关的业务逻辑。*/@Autowiredprivate RoleService roleService = null;/*** 处理GET请求,根据角色ID获取角色信息。* @param id 角色的唯一标识符。* @return 对应于请求ID的角色对象。*/@GetMapping(value = "/info/{id}")@ResponseBodypublic Role getRole(@PathVariable("id") Long id) {return roleService.getRole(id);}
}

这样在请求/mvc/role/info/2就可以看到如下的页面了
在这里插入图片描述
服务启动过程中经常会遇到的一个问题是无法创建连接,其中有一种原因是数据库的版本和连接驱动类的版本不匹配,如果使用的是MySQL8.0以下的版本,那在POM中添加的依赖应该是

	<!-- 引入MySQL数据库连接驱动依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.29</version></dependency>

配置数据源的时候,driverClassName配置应该是props.setProperty("driverClassName", "com.mysql.jdbc.Driver");而如果MySQL用的是8.0以上的版本,那么POM中应该添加的依赖是

    <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version> <!-- 使用实际的版本号 --></dependency>

配置数据源的时候,driverClassName配置应该是props.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");

但这是在Spring5基础上的,如果使用低版本的Spring MVC,需要自己创建MappingJackson2HttpMessageConverter(如下代码所示),Spring5之后RequestMappingHandlerAdapter再初始化过程中,会自动注册MappingJackson2HttpMessageConverter对象,所以只需要依赖相关的JSON类库就可以了,自己创建的话代码如下所示;

	/*** 初始化并配置RequestMappingHandlerAdapter,用于处理RESTful API的请求。* @return 配置好的RequestMappingHandlerAdapter实例。*/@Bean(name = "requestMappingHandlerAdapter")public HandlerAdapter initRequestMappingHandlerAdapter() {// 创建RequestMappingHandlerAdapter实例RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();// 创建MappingJackson2HttpMessageConverter实例,用于处理JSON格式的HTTP消息MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();// 定义支持的媒体类型为application/jsonMediaType mediaType = MediaType.APPLICATION_JSON;// 创建支持的媒体类型列表,并添加application/jsonList<MediaType> mediaTypes = new ArrayList<MediaType>();mediaTypes.add(mediaType);// 配置converter支持的媒体类型converter.setSupportedMediaTypes(mediaTypes);// 将converter添加到handler adapter的转换器列表中adapter.getMessageConverters().add(converter);// 返回配置好的handler adapterreturn adapter;}

也可以通过XML创建MappingJackson2HttpMessageConverter

    <!-- 配置处理HTTP请求和响应的适配器 --><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><!-- 配置消息转换器,用于支持JSON格式的请求和响应 --><property name="messageConverters"><list><!-- 引用JSON消息转换器 bean --><ref bean = "converter"/></list></property></bean><!-- 配置JSON消息转换器 --><bean id="converter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><!-- 配置支持的媒体类型,这里指定为UTF-8编码的JSON --><property name="supportedMediaTypes"><list><value>application/json;charset=UTF-8</value></list></property></bean>

SpringMVC的REST风格

为了更好地支持REST风格,Spring MVC4.3之后更新了对REST更多的支持,增加了更多的注解,例如@GetMapping@PostMapping@PutMapping@DeleteMapping@RestController等等,大致可以将这些注解分为两类,其一是映射路由类,包括@GetMapping@PostMapping@PutMapping@DeleteMapping等,其二是标注控制器类,就只有一个@RestController,它将控制器映射方法的返回结果默认为JSON数据集

Rest风格的注解

REST风格映射路由,实际上是使用@GetMapping@PostMapping@PutMapping@DeleteMapping等注解简化@RequestMapping的编写,例如@GetMapping("/info/{id}")就相当于@RequestMapping(value="/info/{id}",method=RequestMethod.GET),对应的其他几个也是类似的等效,但这些注解和@RequestMapping的不同是,它们只能标注在方法上,不能标注在类上

package com.springrest.vo;/*** 结果消息类,用于封装操作结果的成功状态和相关消息。*/
public class ResultMessage {/*** 操作是否成功的标志。*/private Boolean success = false;/*** 操作结果的消息,用于描述操作的具体情况。*/private String message = null;/*** 构造函数,用于创建一个带有成功状态和消息的结果消息对象。** @param success 操作的成功状态。* @param message 操作的结果消息。*/public ResultMessage(Boolean success, String message) {this.success = success;this.message = message;}/*** 默认构造函数,用于创建一个成功状态为false,消息为空的结果消息对象。*/public ResultMessage() {}/*** 获取操作成功的标志。** @return 操作成功的布尔值。*/public Boolean getSuccess() {return success;}/*** 设置操作成功的标志。** @param success 操作的成功状态。*/public void setSuccess(Boolean success) {this.success = success;}/*** 获取操作结果的消息。** @return 操作结果的消息字符串。*/public String getMessage() {return message;}/*** 设置操作结果的消息。** @param message 操作结果的消息。*/public void setMessage(String message) {this.message = message;}}
package com.springrest.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;import com.springrest.pojo.Role;
import com.springrest.service.RoleService;
import com.springrest.vo.ResultMessage;@RestController
@RequestMapping("/role2")
public class RoleControllerII {@Autowiredprivate RoleService roleService = null;@GetMapping("/page")public ModelAndView page() {ModelAndView mv = new ModelAndView("restful");return mv;}@GetMapping("/info/{id}")public Role getRole(@PathVariable("id") Long id) {return roleService.getRole(id);}@PostMapping("/")public ResultMessage newRole(@RequestBody Role role) {Integer result = roleService.insertRole(role);if (result > 0) {return new ResultMessage(true, "新增角色成功,编号为:" + role.getId());}return new ResultMessage(false, "新增角色失败!");}@PutMapping("/")public ResultMessage updateRole(@RequestBody Role role) {Integer result = roleService.updateRole(role);if (result > 0) {return new ResultMessage(true, "修改角色成功,编号为:" + role.getId());}return new ResultMessage(false, "修改角色失败!");}@DeleteMapping("/{id}")public ResultMessage deleteRole(@PathVariable("id") Long id) {Integer result = roleService.deleteRole(id);if (result > 0) {return new ResultMessage(true, "删除角色成功,编号为:" + id);}return new ResultMessage(false, "新增角色失败!编号为" + id);}
}

在Controller类上标注了@RestController,表示该控制器将采用REST风格,其他的URI都采用了REST风格设计,这里需要特别注意的是public ModelAndView page()方法,它返回的是ModelAndView对象,而不是字符串,因为标注了@RestController后,视图解析器就失去了解析字符串的能力,必须使用ModelAndView才能定位到视图,而page返回一个的是一个字符串"restful",它指向一个/WEB-INF/jsp/JSP文件,源码如下

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>REST风格测试</title><script type="text/javascript"src="https://code.jquery.com/jquery-3.2.1.min.js"></script><script type="text/javascript"><!-- 此处加入对应的JavaScript脚本,进行测试 -->function post() {var role = {'roleName' : 'role_name_new','note' : "note_new"};$.post({url : "./",//此处需要告知传递参数类型为JSON,不能缺少contentType : "application/json",//将JSON转化为字符串传递data : JSON.stringify(role),//成功后的方法success : function(result) {if (result == null || result.success == false) {alert("插入失败");return;}alert(result.message);}});}post();function put() {var role = {'id' : 15,'roleName' : 'role_name_update','note' : "note_update"};$.ajax({url : "./", // 此处告知使用PUT请求type :'PUT', //此处需要告知传递参数类型为JSON,不能缺少contentType : "application/json",//将JSON转化为字符串传递data : JSON.stringify(role),success : function(result, status) {if (result == null) {alert("结果为空")} else {alert(JSON.stringify(result));}}});}put();function del() {var id = 17;$.ajax({url : "./" + id, // 告知请求类型为“DELETE”type :'DELETE', success : function(result) {if (result == null) {alert("后台出现异常。")} else {alert(result.message);}}});}del();</script></head><body></body>
</html>

JSP文件中加入了JQuery脚本,使用它来简化验证新建的控制器,例如使用了$.post(...)对后端发送Ajax请求,它代表发送POST请求到后端,并组织了一个媒体类型为JSON的请求体发送到后端,这很显然就能匹配上控制器的newRole方法,其他的请求同理

以上是一些正常情况,但经常会出现后端不能正常返回的情况,比如尝试访问编号为1000的角色信息,但数据库中并没有这个角色,用如下代码模拟

        function get() {var id = 1000;// 通过GET请求获取角色信息$.get("./info/" + id,  function(role) { alert("role_name-> " + role.roleName); });}get();

如果角色不存在而返回一个空值,很显然不是友好的结果,比较好的处理是提示用户不存在,实际上使用HTTP请求会有响应码,比如200,比如POST请求创建资源的响应码201分别表示成功,使用响应码比较简单,Spring中提供了枚举类HttpStatus定义各种HTTP响应码,同时提供了注解@ResponseStatus, 修改一下控制器的newRole方法

    /*** 通过POST请求创建新角色。** @param role 包含新角色信息的请求体。* @return 如果角色创建成功,返回包含成功消息和角色ID的结果消息;如果创建失败,返回失败消息。*/@PostMapping("/")// 定义响应码为创建成功(201)@ResponseStatus(HttpStatus.CREATED)public ResultMessage newRole(@RequestBody Role role) {// 调用角色服务插入新角色Integer result = roleService.insertRole(role);// 根据插入结果判断角色创建是否成功if (result > 0) {// 如果插入成功,返回成功消息和角色IDreturn new ResultMessage(true, "新增角色成功,编号为:" + role.getId());}// 如果插入失败,返回失败消息return new ResultMessage(false, "新增角色失败!");}

然后执行到这个方法的时候会看到如下的信息
在这里插入图片描述
使用注解@ResponseStatus,得到了状态码201,状态码比响应码更准确,可以通过状态码确定结果是否正确,这样客户端便可以通过状态码分析请求的结果,然而仅仅有状态码是不够的,有时候请求的失败是后端的限制造成的,比如请求编号为200的角色对象,事实上如果它根本不存在,这个时候应该把状态和原因插入响应头,这样请求者就能更明确地知道原因,并能更便利且直接的提示给用户,处理此类问题,SpringMVC提供了类ResponseEntity<T>,这个类存在3个属性status:HttpStatus类型,表示响应码,headers:HTTP响应头,可以自定义消息,body:响应体,HTTP请求响应的正文

		/*** 执行删除操作的函数。* 该函数通过AJAX请求向服务器发送一个DELETE请求,以删除指定的资源。* 请求成功后,根据服务器返回的结果展示相应的提示信息。*/function del() {// 定义待删除资源的IDvar id = 17;// 发起AJAX请求$.ajax({// 构造请求的URL,基于当前路径和资源IDurl : "./" + id,// 告知请求类型为“DELETE”type :'DELETE',// 请求成功回调函数success : function(result) {// 判断服务器返回的结果是否为空if (result == null) {// 如果结果为空,提示“后台出现异常”alert("后台出现异常。")} else {// 如果结果不为空,显示服务器返回的提示信息alert(result.message);}},// 请求错误回调函数,当执行Ajax请求返回500时,则执行error属性对应的如下方法error:function (request, textStatus, errorThrown){// 显示请求错误的提示信息alert('访问后端失败'+ errorThrown)}});}

上边这段代码中,有个error对应的函数,如果请求错误就会回调该函数,就是当执行Ajax请求返回500时,则执行error属性对应的方法,然而同类的还有很多状态码1xx、2xx、3xx、4xx、5xx等如果每个都这么写代码就会相当复杂

通过status可以设置HTTP的响应码,而一般来说可以设置为200,即便产生错误请求也可以设置为200,这样有利于客户端的编写;Header属性可以设置一些值,作为服务器后端的返回信息,例如设置"success"属性表示该请求是否正常,如果不正常再通过属性"message"告诉服务器后端的问题是什么,这样更有利于客户端的编写,在Controller中新增方法,代码如下

    @GetMapping("/info2/{id}")public ResponseEntity<Role> getRole2(@PathVariable("id") Long id) {// 响应体Role body = roleService.getRole(id);// 响应头HttpHeaders headers = new HttpHeaders();if (body != null) { // 获取角色成功headers.add("success", "true");headers.add("message", "ok!!");} else { // 获取角色失败headers.add("success", "false");headers.add("message", "no id=[" + id + "] role info!!");}// 创建ResponseEntityResponseEntity<Role> roleEntity = new ResponseEntity<>(body, headers, HttpStatus.OK);return roleEntity;}

然后用JQuery模拟请求,代码如下

        function get2() {var id = 200;$.ajax({type: "get",url: './info2/' +id,success: function(role,status,xhr) {// 获取响应头var success = xhr.getResponseHeader("success");// 通过响应头判定获取失败if ("false" == success) { // 响应错误信息var message = xhr.getResponseHeader("message");alert(message);} else { // 获取结果成功alert(role.roleName)}}});}get2();

在执行到对应的控制器方法后,会的到如下信息
在这里插入图片描述
这样前端也可以更好的利用这些资源,但会增加一些代码量,如果有很好的开发规范,也不算什么大问题

RestTemplate

在当今的架构中,微服务已经是主流,而在微服务中,会将一个很大的系统拆分为多个子系统,REST风格请求是系统之间交互的基本方式,通常情况下各子系统或者各服务会以HTTP的REST风格暴露服务接口,各子系统或服务之间通过RestTemplate进行服务调用完成交互的目的,而SpringMVC提供的RestTemplate的作用是简化调用过程的,实例代码如下

package com.springrest.rest.client;import java.util.HashMap;
import java.util.Map;import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;import com.springrest.pojo.Role;
import com.springrest.vo.ResultMessage;public class RestTemplateDemo {// 创建RestTemplateprivate static RestTemplate restTemplate = new RestTemplate();// 基础HTTP请求路径private static String baseUrl = "http://localhost:8080/springrest_war/mvc";public static void main(String[] args) {
//		testGet();
//		testPost();
//		testDelet();
//		testPut();
//		testEnity();exchange();}/*** 通过HTTP GET请求获取角色信息。* 此方法演示了如何使用RestTemplate从指定的URL获取特定角色的信息。* 它通过将URL模板与特定ID结合使用,构建一个请求URL,并期望返回一个Role对象。* @see Role 用于表示角色的数据类。* @see RestTemplate 用于执行RESTful请求的Spring框架类。*/private static void testGet() {// 构建请求URL,其中{id}是一个占位符,用于动态插入角色ID。String url = baseUrl + "/role2/info/{id}";// 使用RestTemplate的getForObject方法,从指定URL获取Role对象。// 1L是参数,代替URL中的{id}占位符Role role = restTemplate.getForObject(url, Role.class, 1L);// 输出角色名称。System.out.println(role.getRoleName());}/*** 使用RESTful API进行角色信息的创建测试。* 通过POST请求向指定URL发送角色信息,期望返回操作结果的消息。* 这个方法展示了如何使用Spring的RestTemplate类进行HTTP请求,以及如何处理响应。*/private static void testPost() {// 初始化HTTP请求头,指定请求内容类型为JSON。HttpHeaders headers = new HttpHeaders();// 设置请求内容为JSON类型headers.setContentType(MediaType.APPLICATION_JSON);// 创建一个新的角色实例,设置角色的名称和备注。Role role = new Role("tmpl_name", "tmpl_note");// 构建HTTP请求实体,包含角色信息和请求头,role作为请求体对象HttpEntity<Role> request = new HttpEntity<>(role, headers);// 拼接URL,指定请求的资源路径。String url = baseUrl + "/role2/";// 这里使用了RestTemplate的postForObject方法,该方法用于发送POST请求并解析响应,期望返回一个ResultMessage对象,其中包含操作结果的消息。ResultMessage resultMsg = restTemplate.postForObject(url, request, ResultMessage.class);// 输出操作结果的消息。System.out.println(resultMsg.getMessage());}/*** 测试删除操作。* 该方法通过发送一个DELETE请求到指定的URL来删除一个角色。* 删除操作的特定目标由URL中的{id}占位符和请求参数中的"id"值共同确定。* 使用REST模板的delete方法简化了HTTP删除请求的发送过程。*/private static void testDelete() {// 初始化请求参数映射,用于传递删除操作的特定ID。Map<String, Object> params = new HashMap<>();params.put("id", 20);// 构建请求的URL,其中包括基础URL和动态部分{id}。String url = baseUrl + "/role2/{id}";// 发送DELETE请求到指定URL,带上参数。restTemplate.delete(url, params);}/*** 测试使用REST模板更新角色信息的方法。* 该方法通过构造HTTP请求,包括请求头和请求体,来更新指定角色的信息。* 请求体中包含了角色的名称和备注信息,以及要更新的角色ID。* 使用REST模板的put方法发送PUT请求到指定的URL,完成角色信息的更新。*/private static void testPut() {// 初始化HTTP请求头,指定请求内容类型为JSONHttpHeaders headers = new HttpHeaders();// 设置请求内容为JSON类型headers.setContentType(MediaType.APPLICATION_JSON);// 创建一个新的角色对象,设置角色的名称、备注和IDRole role = new Role("u_tmpl_name", "u_tmpl_note");role.setId(19L);// 构造包含请求头和请求体的HTTP请求实体,role作为请求体对象HttpEntity<Role> request = new HttpEntity<>(role, headers);// 拼接角色信息更新的URLString url = baseUrl + "/role2/";// 使用REST模板发送PUT请求,更新角色信息restTemplate.put(url, request);}/*** 测试通过RESTful API获取实体对象。* 该方法通过发送HTTP GET请求到指定URL来获取一个Role实体。如果请求成功,它将打印出角色名称;* 如果请求失败,它将打印出错误消息。* 使用RestTemplate类来发送HTTP请求,并通过ResponseEntity来处理响应,包括响应体和响应头信息。*/private static void testEnity() {// 构建请求URL,其中{id}是一个占位符,用于动态插入角色ID。String url = baseUrl + "/role2/info2/{id}";// 指定要查询的角色ID。Long id = 1L;// 发送GET请求并获取响应实体。ResponseEntity<Role> roleEntity = restTemplate.getForEntity(url, Role.class, id);// 从响应头中获取"success"字段,判断请求是否成功。String success = roleEntity.getHeaders().get("success").get(0);// 将"success"字段的值转换为boolean类型。boolean flag = Boolean.parseBoolean(success);// 如果请求成功。if (flag) { // 获取成功// 提取响应体中的Role对象。Role role = roleEntity.getBody();// 打印角色名称。System.out.println(role.getRoleName());} else {// 如果请求失败,从响应头中获取"message"字段,获取后端响应头信息String message = roleEntity.getHeaders().get("message").get(0);System.out.print(message);}}/*** 调用API交换角色信息。* 该方法通过PUT请求更新指定角色的信息。它构造请求URL、请求头和请求体,然后发送请求。* 请求体是一个Role对象,包含要更新的角色名称和备注信息。* 方法打印出API响应中的消息部分。*/private static void exchange() {// 构造请求的URLString url = baseUrl + "/role2/";// 初始化HTTP请求头,指定请求内容类型为JSONHttpHeaders headers = new HttpHeaders();// 设置请求内容为JSON类型headers.setContentType(MediaType.APPLICATION_JSON);// 创建一个新的角色对象,设置角色的名称、备注和IDRole role = new Role("u_tmpl_name", "u_tmpl_note");role.setId(19L);// 将角色对象和请求头封装成一个HttpEntity对象,作为PUT请求的请求体HttpEntity<Role> request = new HttpEntity<>(role, headers);// 发送PUT请求,更新角色信息,并接收响应// 使用更为底层的exchange方法执行请求ResponseEntity<ResultMessage> result = restTemplate.exchange(url, HttpMethod.PUT, request, ResultMessage.class);// 打印响应体中的消息字段System.out.println(result.getBody().getMessage());}}

RestTemplate的PUT和DELETE请求都不返回结果,因此无法鉴别服务调用的成败,但大部分调用都需要鉴别请求结果,正如前边的代码将HTTP请求的结果返回ResponseEntity<T>一样,对此RestTemplate也给予了支持,正如private static void testEnity() 方法中所写,此外RestTemplate还提供了一个底层的exchange方法,通过这个方法也可以获取PUT请求返回的ResponseEntity<T>,正如代码中private static void exchange()方法所写

exchange方法还支持GET,HEAD,POST,PATCH,DELETE,OPTIONS,TRACE;,具体情况可以直接看HttpMethod的源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/30305.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CSS in Depth2精译】1.1.4 源码顺序

解决层叠冲突的最后一环叫做 源码顺序&#xff0c;有时又称为 出现顺序&#xff08;order of appearance&#xff09;。如果其他判定规则均一致&#xff0c;则样式表中后出现的、或者在页面较晚引入的样式表声明&#xff0c;将最终胜出。 也就是说&#xff0c;可以通过控制源码…

textarea标签改写为富文本框编辑器KindEditor

下载 - KindEditor - 在线HTML编辑器 KindEditor的简单使用-CSDN博客 一、 Maven需要的依赖&#xff1a; 如果依赖无法下载&#xff0c;可以多添加几个私服地址&#xff1a; 在Maven框架中加入镜像私服 <mirrors><!-- mirror| Specifies a repository mirror site to…

免费定位服务方案:华为定位+天地图逆地理编码实现位置信息查询

对于Android开发来说进行定位开发时会使用以下几个产品 高德定位sdk百度定位sdk腾讯定位sdk 由于这几款产品在商用时需要支付相应的费用&#xff0c;如果不使用这几款产品又该如何定位呢&#xff1f; 有一种解决方案就是 华为定位天地图逆地理编码实现位置信息查询 通过 华…

产品交付能力提升的探索与分享

在当前激励的市场竞争环境下&#xff0c;对项目交付的成本和毛利要求越来越高。如何能快速高效地完成项目交付已然成为我们矢志追求的目标。抛开人为因素对项目交付效率的影响&#xff0c;产品本身的交付能力才是关键。因此&#xff0c;在设计新产品时需要考虑其便捷交付性&…

【C++初阶路】--- 类和对象(中)

目录 一、this指针1.1 this指针的引出1.2 this指针的特性1.3. C语言和C实现Stack的对比 二、类的6个默认成员函数三、构造函数3.1 概念3.2 特性 一、this指针 1.1 this指针的引出 如下定义一个日期类Date class Date { public://void InitDate(Date* const this, int year …

COGNEX康耐视 INsight Micro系列视觉系统安装手测

COGNEX康耐视 INsight Micro系列视觉系统安装手测

【html】用html+css模拟Windows右击菜单

效果图&#xff1a; 在这个示例中&#xff0c;我为每个.second-list添加了一个.sub-menu的<div>&#xff0c;它包含了子菜单项。当鼠标悬停在.second-list上时&#xff0c;.sub-menu会显示出来。你可以根据需要调整这个示例以适应你的具体需求。 记住&#xff0c;这只是…

[学习笔记]-MyBatis-Plus简介

简介 Mybatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 简言之就是对单表的增删改查有了很好的封装。基本不用再单独写sql语句了。目前此类…

海外优青ppt美化_海优ppt录音视频制作

海外优青 优秀青年科学基金项目&#xff08;海外&#xff09;旨在吸引和鼓励在自然科学、工程技术等方面已取得较好成绩的海外优秀青年学者&#xff08;含非华裔外籍人才&#xff09;回国&#xff08;来华&#xff09;工作&#xff0c;自主选择研究方向开展创新性研究&#xf…

云商崆峒乐购618活动2024:企业联动创辉煌

2024年6月18日&#xff0c;云商崆峒乐购618活动在平凉盛大开幕。本次活动由崆峒区商务局、崆峒区电子商务协会与平凉新世纪柳湖春酒业公司联合举办&#xff0c;旨在借助“618”全民线上欢购的热潮&#xff0c;整合平凉本地名优特产&#xff0c;推动崆峒区电商产业及特色网货的发…

mouseinc-smartUp Gestures被禁用后的替代品

前言 smartUp Gestures恶意软件,既然谷歌这么判断,可能大概率没错了,我们换一个mouseInc吧下载地址 https://www.123pan.com/s/fDzUVv-hCtlA 设置下会更好用 设置 通过AHK设置下一些快捷操作~ 对应的查找 https://source.chromium.org/chromium/chromium/src//main:chrome/a…

Elasticsearch安装(windows)

先给出网址 elasticsearch&#xff1a;Download Elasticsearch | Elastic elasticKibana&#xff1a;Download Kibana Free | Get Started Now | Elastic Logstash&#xff1a;Download Logstash Free | Get Started Now | Elastic ik分词&#xff1a;Releases infinilabs/…

adb卸载系统应用

1.进入shell adb shell2.查看所有包 pm list packages3.查找包 如查找vivo相关的包 pm list packages | grep vivo发现包太多了,根本不知道哪个是我们想卸载的应用 于是可以打开某应用,再查看当前运行应用的包名 如下: 4.查找当前前台运行的包名 打开某应用,在亮屏状态输入 …

NX GC工具箱 替换模板标注及中心线丢失

NX GC工具箱 替换模板标注及中心线丢失 前期已对制图模板做了修改优化&#xff0c;170图框层&#xff0c;171零件视图层&#xff0c;172中心线层&#xff0c;173标注层。 GC工具箱替换模板原理是删除原指定图层&#xff0c;再添加模板到里面。 1.打开GC工具箱配置文件&#x…

在哪可以查到全网的司法诉讼信息?

司法涉诉信息指的是再司法活动中形成的各种记录和资料&#xff0c;涵盖了诉讼案件的立案&#xff0c;审判&#xff0c;执行等各个环节的记录和文件。比如基本案件信息&#xff0c;开庭信息&#xff0c;审判信息&#xff0c;执行信息等。有时候还会涉及到被执行人&#xff0c;司…

小白如何重装系统win10?电脑一键重装系统傻瓜式操作!超详细步骤!

随着电脑的广泛应用&#xff0c;给笔记本/台式电脑系统重装已成为一项基本技能。对于电脑新手而言&#xff0c;如何重装Win10系统&#xff0c;或者更高版本的Win11系统可能是一个巨大的挑战。如果对电脑重装系统刚好有需要了解的小伙伴&#xff0c;不妨看看下面的干货分享。本文…

MySQL版本发布模型

MySQL 8.0 之后使用了新的版本控制和发布模型&#xff0c;分为两个主线&#xff1a;长期支持版&#xff08;LTS&#xff09;以及创新版。这两种版本都包含了缺陷修复和安全修复&#xff0c;都可以用于生产环境。 下图是 MySQL 的版本发布计划&#xff1a; 长期支持版 MySQL…

java 线程之间通信-volatile 和 synchronized

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

欢度盛夏,畅享清凉——七月超市营销策略

随着七月的到来&#xff0c;我国大部分地区进入夏季&#xff0c;气温逐渐攀升&#xff0c;消费者们对清凉、消暑产品的需求也随之增长。在这个夏日&#xff0c;超市应该如何抓住这一商机&#xff0c;提升销售业绩呢&#xff1f;本文将从商品陈列、促销活动等方面&#xff0c;为…