1、认识REST
1.1 什么是REST
REST是软件架构的规范体系结构,它将资源的状态以适合客户端的形式从服务器端发送到客户端(或相反方向)。在REST中,通过URL进行资源定位, 用HTTP动作(GET、POST、DELETE、PUSH等)描述操作,完成功能。
遵循RESTful风格,可以使开发的接口通用,以便调用者理解接口的作用。基于REST构建的API就是RESTful (REST风格) API。各大机构提供的API基本都是RESTful风格的。这样可以统一规范, 减少沟通、学习和开发的成本。
1.2 HTTP方法与CRUD动作映射
RESTful风格使用同一个URL,通过约定不同的HTTP方法来实施不同的业务。
普通网页的CRUD和RESTful风格的CRUD的区别,如下表:
动作 | 普通CRUD的URL | 普通CRUD的HTTP方法 | RESTful的URL | RESTful的CRUD的HTTP方法 |
---|---|---|---|---|
查询 | user/id=1 | GET | user/{id} | GET |
增加 | user?name=xxx | GET/POST | user | POST |
修改 | user/update?id=1 | GET | user/{id} | PUT 或 PATCH |
删除 | user/delete?id=1 | GET | user/{id} | DELETE |
可以看出,RESTful风格的CRUD比传统的CRUD简单明了,它通过HTTP方法来区分查询、增加、修改、删除。
2、创建项目与数据表
2.1 创建项目
(1)创建SpringBoot项目,项目结构如下图:
(2)添加pom.xml配置信息
由于实例中使用到了JPA,所以需要添加JPA和MySQL数据库的依赖:
<!-- Spring Data JPA依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL的JDBC数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
(3)配置数据库连接信息
SpringBoot项目使用MySQL等关系型数据库,需要配置连接信息。
将默认的application.properties文件的后缀修改为“.yml”,即配置文件名称为:application.yml,并配置以下MySQL数据库的连接信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database: MYSQL
show-sql: true
open-in-view: true
properties:
hibernate:
enable_lazy_load_no_trans: true #使用延时加载时控制Session的生命周期
dialect: org.hibernate.dialect.MySQL5Dialect
ddl-auto: update
2.2 创建数据库表
使用MySQL数据库,创建 tb_user 用户信息表,并添加数据。
-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tb_user;
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tb_user
(
user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
user_name VARCHAR(50) NOT NULL COMMENT '用户姓名',
blog_url VARCHAR(50) NOT NULL COMMENT '博客地址',
blog_remark VARCHAR(50) COMMENT '博客信息'
) COMMENT = '用户信息表';
-- 添加数据
INSERT INTO tb_user(user_name,blog_url,blog_remark) VALUES('pan_junbiao的博客','https://blog.csdn.net/pan_junbiao','您好,欢迎访问 pan_junbiao的博客');
INSERT INTO tb_user(user_name,blog_url,blog_remark) VALUES('pan_junbiao的博客_02','https://blog.csdn.net/pan_junbiao','您好,欢迎访问 pan_junbiao的博客');
3、设计统一的RESTful风格的应用
近年来,随着移动互联网的发展,各种类型的客户端层出不穷。如果不统一数据接口,则会造成冗余代码,增加成本。RESTful风格的API正适合通过一套统一的接口为PC、手机APP等设备提供数据服务。
3.1 创建实体类(Entity层)
创建UserInfo类(用户信息的持久化类)。
import javax.persistence.*;
/**
* 用户信息的持久化类
* @author pan_junbiao
**/
@Entity
@Table(name = "tb_user")
public class UserInfo
{
//用户ID
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private int userId;
//用户姓名
@Column(name = "user_name")
private String userName;
//博客地址
@Column(name = "blog_url")
private String blogUrl;
//博客信息
@Column(name = "blog_remark")
private String blogRemark;
//省略getter与setter方法...
}
3.2 定义统一返回的格式(Model层)
创建ApiStatusCode枚举(API执行状态编码枚举)。
/**
* API执行状态编码枚举
* @author pan_junbiao
**/
public enum ApiStatusCode
{
SUCCESS(2000, "操作成功"),
NO_ACCESS(4000,"无访问权限"),
BUSINESS_EXCEPTION(5000,"业务异常"),
PARAM_INVALID(5001,"参数缺失或无效"),
NOT_LOGIN_IN(5002,"未登录"),
SYSTEM_EXCEPTION(6000,"系统异常"),
FAILED(9000,"操作失败");
private final int code; //状态码
private final String description; //描述
private ApiStatusCode(int code, String description)
{
this.code = code;
this.description = description;
}
public int getCode()
{
return code;
}
public String getDescription()
{
return description;
}
}
创建ApiResultModel类(Api返回结果类)。
/**
* Api返回结果类
* @author pan_junbiao
**/
public class ApiResultModel
{
public int statusCode; //状态码
public String message; //返回消息描述
public Object data; //返回数据
public int getStatusCode()
{
return statusCode;
}
public void setStatusCode(int statusCode)
{
this.statusCode = statusCode;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
}
3.3 实现数据库访问接口层(Dao层)
创建UserDao.java(用户信息数据库访问接口),并继承JpaRepository接口。
import com.pjb.entity.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 用户信息数据库访问接口
* @author pan_junbiao
**/
@Repository
public interface UserDao extends JpaRepository<UserInfo,Integer>
{
}
3.4 实现控制器方法(Controller层)
创建UserController(用户控制器),实现用户数据的查询、新增、修改、删除,并实现数据的返回。
import com.pjb.dao.UserDao;
import com.pjb.entity.UserInfo;
import com.pjb.model.ApiResultModel;
import com.pjb.model.ApiStatusCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户控制器
* @author pan_junbiao
**/
@RestController
@RequestMapping("user")
public class UserController
{
@Autowired
private UserDao userDao;
/**
* 获取用户列表
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public ApiResultModel getUserList()
{
//获取用户列表
List<UserInfo> userInfoList = userDao.findAll();
//返回结果
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.SUCCESS.getCode();
apiResultModel.message = ApiStatusCode.SUCCESS.getDescription();
apiResultModel.data = userInfoList;
return apiResultModel;
}
/**
* 获取用户信息
*/
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ApiResultModel getUserInfo(@PathVariable("id")int userId)
{
//根据用户ID,获取用户信息
UserInfo userInfo = userDao.findById(userId).orElse(null);
//返回结果
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.SUCCESS.getCode();
apiResultModel.message = ApiStatusCode.SUCCESS.getDescription();
apiResultModel.data = userInfo;
return apiResultModel;
}
/**
* 新增用户信息
*/
@RequestMapping(value = "/", method = RequestMethod.POST)
public ApiResultModel addUser(@RequestBody UserInfo userInfo)
{
//参数验证
if(userInfo.getUserName()==null || userInfo.getUserName().length()==0)
{
//返回参数缺失
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.PARAM_INVALID.getCode();
apiResultModel.message = ApiStatusCode.PARAM_INVALID.getDescription();
apiResultModel.data = false;
return apiResultModel;
}
//新增用户信息
userDao.save(userInfo);
//返回结果
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.SUCCESS.getCode();
apiResultModel.message = ApiStatusCode.SUCCESS.getDescription();
apiResultModel.data = true;
return apiResultModel;
}
/**
* 修改用户信息
*/
@RequestMapping(value = "/", method = RequestMethod.PUT)
public ApiResultModel updateUser(@RequestBody UserInfo userInfo)
{
//参数验证
if(userInfo.getUserId()<=0 || userInfo.getUserName()==null || userInfo.getUserName().length()==0)
{
//返回参数缺失
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.PARAM_INVALID.getCode();
apiResultModel.message = ApiStatusCode.PARAM_INVALID.getDescription();
apiResultModel.data = false;
return apiResultModel;
}
//修改用户信息
userDao.save(userInfo);
//返回结果
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.SUCCESS.getCode();
apiResultModel.message = ApiStatusCode.SUCCESS.getDescription();
apiResultModel.data = true;
return apiResultModel;
}
/**
* 删除用户信息
*/
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ApiResultModel deleteUserInfo(@PathVariable("id")int userId)
{
//删除用户信息
userDao.deleteById(userId);
//返回结果
ApiResultModel apiResultModel = new ApiResultModel();
apiResultModel.statusCode = ApiStatusCode.SUCCESS.getCode();
apiResultModel.message = ApiStatusCode.SUCCESS.getDescription();
apiResultModel.data = true;
return apiResultModel;
}
}
4、全局异常处理
Spring提供了非常方便的异常处理方案——控制器通知 (@ControllerAdvice 或 @RestControllerAdvice),它将所有控制器作为一 个切面,利用切面技术来实现。
通过基于@ControllerAdvice或@RestContollerAdvice的注解可以对异常进行全局统一处理,默认对所有的Controller有效。
这里由于是RESTful接口架构应用,所以使用@RestContollerAdvice注解来统一处理异常。
4.1 创建异常结果类
创建ExceptionResult类(异常结果类)。
/**
* 异常结果类
* @author pan_junbiao
**/
public class ExceptionResult
{
public int statusCode; //状态码
public String message; //返回消息描述
public int getStatusCode()
{
return statusCode;
}
public void setStatusCode(int statusCode)
{
this.statusCode = statusCode;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
}
4.2 自定义业务异常类
创建BusinessException类(业务异常类),继承RuntimeException类。
/**
* 业务异常类
* @author pan_junbiao
**/
public class BusinessException extends RuntimeException
{
private String errorMessage; //异常信息
public BusinessException(String errorMessage)
{
super(errorMessage);
this.errorMessage = errorMessage;
}
public String getErrorMessage()
{
return errorMessage;
}
public void setErrorMessage(String errorMessage)
{
this.errorMessage = errorMessage;
}
}
4.3 创建全局异常处理器
创建BusinessExceptionHandler类(全局异常处理器)。
import com.pjb.model.ApiStatusCode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
* @author pan_junbiao
**/
@RestControllerAdvice
public class BusinessExceptionHandler
{
//日志对象
private Logger logger = LogManager.getLogger(BusinessExceptionHandler.class);
/**
* 自定义业务异常处理
*/
@ExceptionHandler(BusinessException.class)
public ExceptionResult businessExceptionHandler(BusinessException ex)
{
//错误信息
String errorMessage = ex.getErrorMessage();
//记录异常日志
logger.error("[业务异常]" + errorMessage);
//返回错误信息
ExceptionResult exceptionResult = new ExceptionResult();
exceptionResult.statusCode = ApiStatusCode.BUSINESS_EXCEPTION.getCode();
exceptionResult.message = errorMessage;
return exceptionResult;
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public ExceptionResult exceptionHandler(Exception ex)
{
//记录异常日志
logger.error("[系统异常]" + ex.toString());
//返回错误信息
ExceptionResult exceptionResult = new ExceptionResult();
exceptionResult.statusCode = ApiStatusCode.SYSTEM_EXCEPTION.getCode();
exceptionResult.message = "系统错误,请联系管理员";
return exceptionResult;
}
}
【示例】测试全局异常功能。
/**
* 自定义异常测试
* @author pan_junbiao
*/
@RequestMapping("/exceptionTest")
public void exceptionTest()
{
throw new BusinessException("自定义异常信息");
}
执行结果:
5、运行测试
5.1 使用ajax请求
使用JQuery提供的Ajax功能请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户操作</title>
<meta name="author" content="pan_junbiao的博客">
<style>
table { border-collapse: collapse; margin-bottom: 10px}
table,table tr th, table tr td { border:1px solid #000000; padding: 5px 10px;}
</style>
</head>
<body>
<div align="center">
<table>
<caption>用户信息</caption>
<tr>
<th>用户ID:</th>
<td id="td_userId"></td>
</tr>
<tr>
<th>用户名称:</th>
<td id="td_userName"></td>
</tr>
<tr>
<th>博客地址:</th>
<td id="td_blogUrl"></td>
</tr>
<tr>
<th>博客信息:</th>
<td id="td_blogRemark"></td>
</tr>
</table>
<input type="button" id="btnGetUser" value="获取用户"/>
<input type="button" id="btnAddUser" value="新增用户"/>
<input type="button" id="btnUpdateUser" value="修改用户"/>
<input type="button" id="btnDeleteUser" value="删除用户"/>
</div>
</body>
<script src="/js/jquery-3.4.1.min.js"></script>
<script>
//获取用户
$("#btnGetUser").click(function ()
{
//执行Ajax请求
$.ajax({
type: "GET",
url: "http://127.0.0.1:8080/user/1",
contentType: "application/json; charset=UTF-8",
success: function (result)
{
if(result.statusCode == 2000 && result.data)
{
//绑定数据
$("#td_userId").html(result.data.userId);
$("#td_userName").html(result.data.userName);
$("#td_blogUrl").html(result.data.blogUrl);
$("#td_blogRemark").html(result.data.blogRemark);
}
}
});
});
//新增用户
$("#btnAddUser").click(function ()
{
//请求参数对象
var params = {};
params.userName = "pan_junbiao的博客";
params.blogUrl = "https://blog.csdn.net/pan_junbiao";
params.blogRemark = "您好,欢迎访问 pan_junbiao的博客";
//执行Ajax请求
$.ajax({
type: "POST",
url: "http://127.0.0.1:8080/user/",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(params),
success: function (result)
{
if(result.statusCode == 2000 && result.data == true)
{
alert("新增用户成功")
}
else
{
alert("操作失败:" + result.message);
}
}
});
});
//修改用户
$("#btnUpdateUser").click(function ()
{
//请求参数对象
var params = {};
params.userId = 3;
params.userName = "pan_junbiao的博客_03";
params.blogUrl = "https://blog.csdn.net/pan_junbiao";
params.blogRemark = "您好,欢迎访问 pan_junbiao的博客";
//执行Ajax请求
$.ajax({
type: "PUT",
url: "http://127.0.0.1:8080/user/",
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify(params),
success: function (result)
{
if(result.statusCode == 2000 && result.data == true)
{
alert("修改用户成功")
}
else
{
alert("操作失败:" + result.message);
}
}
});
});
//删除用户
$("#btnDeleteUser").click(function ()
{
//执行Ajax请求
$.ajax({
type: "DELETE",
url: "http://127.0.0.1:8080/user/3",
contentType: "application/json; charset=UTF-8",
success: function (result)
{
if(result.statusCode == 2000 && result.data == true)
{
alert("删除用户成功")
}
else
{
alert("操作失败:" + result.message);
}
}
});
});
</script>
</html>
执行结果:
5.2 使用Postman工具
第二种测试方法,使用Postman工具进行测试。
(1)使用GET方法发送请求获取用户信息,执行结果如下:
(2)使用POST方法发送请求新增用户信息,执行结果如下: