当前位置: 首页 > news >正文

互联网应用主流框架整合之构建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的源码

相关文章:

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

REST&#xff08;Representational State Transfer&#xff09;&#xff0c;中文译为“表述性状态转移”&#xff0c;是由Roy Fielding博士在他的博士论文中提出的一种软件架构风格&#xff0c;特别适用于网络应用的设计。REST不是一个标准&#xff0c;而是一种设计原则和约束集…...

vue3-自定义指令来实现input框输入限制

文章目录 前言具体实现分析主要部分详细解析导入和类型定义mounted 钩子函数unmounted 钩子函数指令注册使用 总结 前言 使用vue中的自定义指令来实现input框输入限制 其中关键代码强制触发input &#xff0c;来避免&#xff0c;输入规则外的字符时&#xff0c;没触发vue的响…...

MySQL日志——redolog

redo log&#xff08;重做日志&#xff09; 为什么需要redo log&#xff1f; 在mysql提交一个事务后&#xff0c;这个事务所作的数据修改并不会直接保存到磁盘文件中&#xff0c;而是先保存在buffer pool缓冲区中&#xff0c;在需要读取数据时&#xff0c;先从缓冲区中找&…...

Python热涨落流体力学求解算法和英伟达人工智能核评估模型

&#x1f3af;要点 &#x1f3af;平流扩散简单离散微分算子 | &#x1f3af;相场模拟&#xff1a;简单旋节线分解、枝晶凝固的 | &#x1f3af;求解二维波动方程&#xff0c;离散化时间导数 &#x1f3af;英伟达 A100 人工智能核性能评估模型 | &#x1f3af;热涨落流体动力学…...

【C语言】数组参数和指针参数详解

在写代码的时候难免要把【数组】或者【指针】传给函数&#xff0c;那函数的参数该如何设计呢&#xff1f; 1 一维数组传参 #include <stdio.h> void test(int arr[])//ok? {} void test(int arr[10])//ok? {} void test(int* arr)//ok? {} void test2(int* arr[20])…...

Tuple 元组

文章目录 一、什么是元组 &#xff1f;二、元组的具体操作2.1 创建元组2.1.1 tuple() 创建元组函数和 list() 创建列表函数总结 2.2 元组的元素访问操作2.3 元组的元素计数操作2.4 zip 对象 一、什么是元组 &#xff1f; 列表属于可变序列,可以任意修改列表中的元素。 元组的…...

(资料收藏)王阳明传《知行合一》共74讲,王阳明知行合一音频讲解资料

今天给大家带来的不是软件&#xff0c;而是一份精神食粮——《知行合一》的教程福利。这可不是一般的教程&#xff0c;它关乎心灵&#xff0c;关乎智慧&#xff0c;关乎我们如何在纷繁复杂的世界中找到自己的位置。 咱们得聊聊王阳明&#xff0c;这位明代的大儒&#xff0c;他…...

空气质量预报模式系统WRF-CMAQ

空气污染问题日益受到各级政府以及社会公众的高度重视&#xff0c;从实时的数据监测公布到空气质量数值预报及预报产品的发布&#xff0c;我国在空气质量监测和预报方面取得了一定进展。随着计算机技术的高速发展、空气污染监测手段的提高和人们对大气物理化学过程认识的深入&a…...

Collections.sort()方法总结

Collections.sort()方法总结 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们来探讨 Java 中的 Collections.sort() 方法。这个方法是 Java 集合框架中的…...

Java23种设计模式(二)

1、单例模式 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有…...

Web前端收入来源:探索多元化的盈利渠道

Web前端收入来源&#xff1a;探索多元化的盈利渠道 在数字化时代&#xff0c;Web前端技术日益成为推动互联网业务发展的重要力量。对于前端开发者而言&#xff0c;除了传统的薪资收入外&#xff0c;还存在多种潜在的收入来源。本文将从四个方面、五个方面、六个方面和七个方面…...

抽象工厂模式(大话设计模式)C/C++版本

抽象工厂模式 C 参考&#xff1a;https://www.cnblogs.com/Galesaur-wcy/p/15927110.html #include <iostream> using namespace std;// 抽象产品Department ,定义具体产品的公共接口 class Department { public:virtual ~Department() default;virtual void Insert()…...

springboot宠物医院信息管理系统-计算机毕业设计源码04164

摘 要 现如今在中国&#xff0c;随着人民生活质量的逐渐提高&#xff0c;以及人民群众消费能力的日渐增长&#xff0c;各种各样的家养小动物&#xff0c;已经逐渐成为人类越来越亲密的生活伴侣。并且&#xff0c;现如今社会竞争及其激烈&#xff0c;人们的生活节奏越发急促、紧…...

Leetcode Hot100之哈希表

1. 两数之和 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现思路…...

Vision Transformer with Sparse Scan Prior

摘要 https://arxiv.org/pdf/2405.13335v1 In recent years, Transformers have achieved remarkable progress in computer vision tasks. However, their global modeling often comes with substantial computational overhead, in stark contrast to the human eye’s eff…...

笔记-python 中BeautifulSoup入门

在前面的例子用&#xff0c;我用了BeautifulSoup来从58同城抓取了手机维修的店铺信息&#xff0c;这个库使用起来的确是很方便的。本文是BeautifulSoup 的一个详细的介绍&#xff0c;算是入门把。文档地址&#xff1a;http://www.crummy.com/software/BeautifulSoup/bs4/doc/ …...

Tomcat Websocket应用实例研究

概述 本文介绍了如何根据Tomcat给出的websocket实例&#xff0c;通过对实例的学习&#xff0c;定制自己基于websocket的应用。 环境及版本&#xff1a; Ubuntu 22.04.4 LTSApache Tomcat/10.1.20openjdk 11.0.23 2024-04-16浏览器&#xff1a;Chrome 相关资源及链接 Class…...

leetcode-11-二叉树前中后序遍历以及层次遍历

一、递归版 前序遍历 &#xff08;先根遍历&#xff09; 中左右 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new ArrayList<Integer>();preorder(root, result);return result;}public void preorder…...

Python基础学习笔记(十一)——集合

目录 一、集合的介绍与创建二、集合的存储原理三、元素的修改1. 添加元素2. 删除元素 四、集合的运算五、集合的判定 一、集合的介绍与创建 集合&#xff08;set&#xff09;&#xff0c;一种可变、无序、不重复的数据结构&#xff0c;由大括号{}内、用逗号分隔的一组元素组成。…...

FineReport

1.FineReport 官网 &#xff1a;FineReport产品简介- FineReport帮助文档 - 全面的报表使用教程和学习资料 下载地址 免费下载FineReport - FineReport报表官网 FineReport是一款用于报表制作&#xff0c;分析和展示的工具。 普通模板&#xff1a;是 FineReport 最常用&#xf…...

嵌入式就业前景好么

嵌入式就业前景在当前环境下是较为乐观的&#xff0c;以下是对嵌入式就业前景的详细分析&#xff1a; 广泛应用领域&#xff1a;嵌入式系统广泛应用于智能家居、医疗设备、航空航天等领域。随着物联网&#xff08;IoT&#xff09;的快速发展&#xff0c;预计到2024年&#xff…...

为啥找对象千万别找大厂男,还好我不是大厂的。。

网上看到一大厂女员工发文说&#xff1a;找对象千万别找大厂男&#xff0c;理由说了一大堆&#xff0c;无非就是大厂男为了逃避带娃&#xff0c;以加班为由宁愿在工位上玩游戏也不愿回家。当然这种观点有的人赞同有的人反对。 网友精彩评论&#xff1a; --------------下面是今…...

如何查看k8s中service的负载均衡策略

在Kubernetes中&#xff0c;Service的负载均衡策略一般由kube-proxy负责&#xff0c;kube-proxy使用iptables或IPVS规则进行负载均衡。默认情况下&#xff0c;kube-proxy使用的是轮询&#xff08;Round Robin&#xff09;策略&#xff0c;但是在使用IPVS模式时&#xff0c;可以…...

Linux-DNS域名解析服务01

BIND 域名服务基础 1、DNS&#xff08;Domain Name System&#xff09;系统的作用及类型 整个 Internet 大家庭中连接了数以亿计的服务器、个人主机&#xff0c;其中大部分的网站、邮件等服务器都使用了域名形式的地址&#xff0c;如 www.google.com、mail.163.com 等。很显然…...

[c++刷题]贪心算法.N01

题目如上: 首先通过经验分析&#xff0c;要用最少的减半次数&#xff0c;使得数组总和减少至一半以上&#xff0c;那么第一反应就是每次都挑数组中最大的数据去减半&#xff0c;这样可以是每次数组总和值减少程度最大化。 代码思路:利用大根堆去找数据中的最大值&#xff0c;…...

推荐常用的三款源代码防泄密软件

三款源代码防泄密软件——安秉源代码加密、Virbox Protector 和 MapoLicensor——确实各自在源代码保护的不同方面有其专长。这些软件可以满足企业对于源代码保护的三大需求&#xff1a;防止泄露、防止反编译和防止破解。 安秉源代码加密&#xff1a; 专注于源代码文件的加密&…...

Android 13 高通设备热点低功耗模式(2)

前言 之前写过一篇文章:高通热点被IOS设备识别为低数据模式,该功能仿照小米的低数据模式写的,散发的热点可以达到被IOS和小米设备识别为低数据模式。但是发现IOS设备如果后台无任何网络请求的时候,息屏的状态下过一会,会自动断开热点的连接。 分析 抓取设备的热点相关的…...

web前端任职条件:全面解析

web前端任职条件&#xff1a;全面解析 在当今数字化快速发展的时代&#xff0c;Web前端技术已经成为互联网行业不可或缺的一部分。作为一名Web前端开发者&#xff0c;需要具备哪些任职条件呢&#xff1f;本文将从四个方面、五个方面、六个方面和七个方面为您深入剖析。 四个方…...

分析医药零售数据该用哪个BI数据可视化工具?

数据是企业决策的重要依据&#xff0c;可以用于现代企业大数据可视化分析的BI工具有很多&#xff0c;各有各擅长的领域。那么哪个BI数据可视化工具分析医药零售数据又好又快&#xff1f; 做医药零售数据分析首推奥威BI数据可视化工具&#xff01; 奥威BI数据可视化工具做医药…...

如何使用芯片手册做软件开发?

在阅读和利用芯片手册进行软件开发时&#xff0c;你应该关注以下几个关键点&#xff1a; 引脚功能&#xff1a;了解芯片上每个引脚的功能&#xff0c;包括它们可以被配置为输入还是输出&#xff0c;以及它们支持的特殊功能&#xff0c;如模拟输入、PWM输出、中断等。 寄存器映…...

做外单要上什么网站/域名查询官网

从EF6/EFCore迁移到Mego框架 如果您有EntityFragmework6或EntityFragmeworkCore的开发经验&#xff0c;在首次接触Mego框架时会发现这两个框架非常相似&#xff0c;本文将帮忙您了解到两者的差异以及从EF6/EFCore迁移到Mego的方法。 Mego本身就是基于EF6与EFCore的思想来开发的…...

网站建设大作业有代码/网络优化的工作内容

//LoginClient.java package mySocket;import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket;/***client通过键盘录入username*服务端对这个username进行校验。**假设该用户存在&#xff0c;在服务端显示xxx…...

做推广比较好的网站/北京营销型网站

基于gentooapachephpmysql的源码包安装&#xff01; 由于gentoo的难度太大&#xff0c;初次学习建议使用红帽linux! lamp编译所需要的软件&#xff01; 1. libxml2-2.6.30.tar.gz 2. libmcrypt-2.5.8.tar.gz 3. zlib-1.2.3.tar.gz 4. libpng-1.2.31.tar.gz 5. jpegsrc.v6b.tar.…...

wordpress 移除菜单/全网引流推广

继承与threading.Thread实现有返回值的子类MyThread&#xff0c;废话不多说&#xff0c;大家直接看代码import threadingclass MyThread(threading.Thread):def __init__(self,func,args()):super(MyThread,self).__init__()self.func funcself.args argsdef run(self):self.…...

网站开发语音/网店推广实训系统

ORACLE PL/SQL编程之把游标说透 http://kb.cnblogs.com/page/100461/ ORACLE PL/SQL编程之把过程与函数说透 http://kb.cnblogs.com/page/100144/ ORACLE PL/SQL编程之把触发器说透 http://kb.cnblogs.com/page/99328/转载于:https://www.cnblogs.com/fangj/archive/2011/11/07…...

深圳手机网站模板/企业网站seo推广方案

上个星期完成了基本功能后&#xff0c;我开始用MFC进行扩展&#xff0c;将它变成一个有图形界面的软件&#xff08;&#xff1f;&#xff09; 扩展之后的程序的对话框和类 Dialog&#xff1a; 1、IDD_ARITHMETIC_DIALOG 介绍&#xff1a;开始界面&#xff0c;需要填入生成表达式…...