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

作者 : admin 本文共17928个字,预计阅读时间需要45分钟 发布时间: 2024-06-16 共1人阅读

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实例,用于处理角色相关的业务逻辑。
	 */
	@Autowired
	private RoleService roleService = null;
	/**
	 * 处理GET请求,根据角色ID获取角色信息。
	 * @param id 角色的唯一标识符。
	 * @return 对应于请求ID的角色对象。
	 */
	@GetMapping(value = "/info/{id}")
	@ResponseBody
	public Role getRole(@PathVariable("id") Long id) {
		return roleService.getRole(id);
	}
}

这样在请求/mvc/role/info/2就可以看到如下的页面了
互联网应用主流框架整合之构建REST风格的系统插图
服务启动过程中经常会遇到的一个问题是无法创建连接,其中有一种原因是数据库的版本和连接驱动类的版本不匹配,如果使用的是MySQL8.0以下的版本,那在POM中添加的依赖应该是

	
	<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/json
		MediaType mediaType = MediaType.APPLICATION_JSON;

		// 创建支持的媒体类型列表,并添加application/json
		List<MediaType> mediaTypes = new ArrayList<MediaType>();
		mediaTypes.add(mediaType);

		// 配置converter支持的媒体类型
		converter.setSupportedMediaTypes(mediaTypes);

		// 将converter添加到handler adapter的转换器列表中
		adapter.getMessageConverters().add(converter);

		// 返回配置好的handler adapter
		return adapter;
	}

也可以通过XML创建MappingJackson2HttpMessageConverter

    
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        
        <property name="messageConverters">
            <list>
                
                <ref bean = "converter"/>
            </list>
        </property>
    </bean>

    
    <bean id="converter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        
        <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 {
@Autowired
private 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) {
// 如果插入成功,返回成功消息和角色ID
return new ResultMessage(true, "新增角色成功,编号为:" + role.getId());
}
// 如果插入失败,返回失败消息
return new ResultMessage(false, "新增角色失败!");
}

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

		/**
* 执行删除操作的函数。
* 该函数通过AJAX请求向服务器发送一个DELETE请求,以删除指定的资源。
* 请求成功后,根据服务器返回的结果展示相应的提示信息。
*/
function del() {
// 定义待删除资源的ID
var id = 17;
// 发起AJAX请求
$.ajax({
// 构造请求的URL,基于当前路径和资源ID
url : "./" + 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!!");
}
// 创建ResponseEntity
ResponseEntity<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();

在执行到对应的控制器方法后,会的到如下信息
互联网应用主流框架整合之构建REST风格的系统插图(2)
这样前端也可以更好的利用这些资源,但会增加一些代码量,如果有很好的开发规范,也不算什么大问题

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 {
// 创建RestTemplate
private 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请求头,指定请求内容类型为JSON
HttpHeaders headers = new HttpHeaders();
// 设置请求内容为JSON类型
headers.setContentType(MediaType.APPLICATION_JSON);
// 创建一个新的角色对象,设置角色的名称、备注和ID
Role role = new Role("u_tmpl_name", "u_tmpl_note");
role.setId(19L);
// 构造包含请求头和请求体的HTTP请求实体,role作为请求体对象
HttpEntity<Role> request = new HttpEntity<>(role, headers);
// 拼接角色信息更新的URL
String 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() {
// 构造请求的URL
String url = baseUrl + "/role2/";
// 初始化HTTP请求头,指定请求内容类型为JSON
HttpHeaders headers = new HttpHeaders();
// 设置请求内容为JSON类型
headers.setContentType(MediaType.APPLICATION_JSON);
// 创建一个新的角色对象,设置角色的名称、备注和ID
Role 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一样,对此RestTemplate也给予了支持,正如private static void testEnity() 方法中所写,此外RestTemplate还提供了一个底层的exchange方法,通过这个方法也可以获取PUT请求返回的ResponseEntity,正如代码中private static void exchange()方法所写

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

本站无任何商业行为
个人在线分享 » 互联网应用主流框架整合之构建REST风格的系统
E-->