从0到1开发一个接口
最近在学习黑马的项目时,突然让手写一个接口,原本以为是个简单的任务,结果发现动手实践起来还是有一些容易忽略的点,本文将从接口设计开始简述开发接口的一般步骤。
接口设计
进入前后端分离式开发时代后,一般是由后端开发人员设计接口并将接口文档提供给前端人员。
不管是什么类型的接口,都是通过HTTP请求来完成由前端发起调用,后端执行程序得到结果返回前端这一过程来实现的。
一个规范的接口应该包含以下内容:
- HTTP请求方法
- 接口路径
- 请求参数类型
- 请求参数内容
- 响应结果类型
- 响应结果状态码
- 响应结果内容
HTTP请求方法
根据RESTful规范:
- 新增方法用POST
- 修改方法用PUT
- 删除方法用DELETE
接口路径
定义为RESTful风格的路径。
- 在类上使用@RequestMapping指定该类中接口的路径的基础路径。
- 在方法中指定具体的路径。
比如:
请求参数类型
常用的有:
- json格式:
- application/json,传递json格式字符串,当传递的参数是属于一个对象的属性时可用此格式,比如:新增、修改时通常传递的数据是某个对象的信息就可以使用此格式。
- 表单格式:
- application/x-www-form-urlencoded,传递key/value串,就是在url后通过?和&进行拼接的参数,比如:/foundations/operation/serve/page?pageNo=1&pageSize=10
- 当传递的参数比较杂且不属于某个特定的对象时使用此格式
请求参数内容
请求参数的内容应当根据需求文档和界面原型去识别。 分页式查询首先有当前页码和每页显示记录数。 对于查询类的接口还有常用的参数有:排序方式、排序字段。
响应结果类型
常见的类型有:text/html、text/plain、application/json等。
响应结果状态码
HTTP状态码是服务器返回给客户端的数字代码(三位数字),共分为五类:
- 1xx: 表示服务器接收到了客户端请求并正在处理
- 2xx: 表示成功状态码
- 3xx:表示重向定状态码
- 4xx:表示客户端错误状态码
- 5xx: 表示服务端错误状态码
当服务端处理成功返回200,其它表示失败。
响应结果内容
分页查询通用的响应内容有:数据列表、总页数、总记录数
数据列表中需要分析具体的属性,根据界面原型进行分析
对于分页查询结果,我们通常将其封装为统一式响应:
参数名称 | 参数说明 | 类型 |
---|---|---|
msg | 响应消息 | string |
code | 状态码,200-成功,其它-失败 | integer(int32) |
data | 分页数据消息体 | |
total | 总条数 | integer(int64) |
pages | 总页数 | integer(int64) |
以下是一个常见的响应示例:
{
"msg": "OK",
"code": 200,
"data": {
"list": [
{
"serveTypeId": 0,
"serveItemName": "",
"updateTime": "",
"saleStatus": 0,
"serveItemId": 0,
"referencePrice": 0,
"createTime": "",
"regionId": 0,
"price": 0,
"id": 0,
"isHot": 0,
"serveTypeName": ""
}
],
"total": 0,
"pages": 0
}
}
实例
页面原型:
接口文档:
根据上面的说明,不难分析出:
- 这是个GET请求
- 接口路径是:/foundations/operation/region/page
- 请求参数类型是:application/x-www-form-urlencoded,即为表单类型:
- 请求参数内容:一般封装为实体:
- 响应结果内容:
接口定义
完成了接口设计之后,就要开始着手书写接口了。
经过第一小节的分析,我们不难写出如下接口:
@RestController("operationServeController")
@RequestMapping("/operation/serve")
@Api(tags = "运营端 - 区域服务相关接口")
public class ServeController {
@Resource
private IServeService serveService;
@GetMapping("/page")
@ApiOperation("区域服务分页查询")
public PageResult<ServeResDTO> page(ServePageQueryReqDTO servePageQueryReqDTO) {
PageResult<ServeResDTO> page = serveService.page(servePageQueryReqDTO);
return page;
}
...
}
注意:当请求参数格式为json需要在方法参数前加@RequestBody注解,这里请求参数类型为from表单格式不用添加此注解。
接下来,使用swagger来初步测试接口功能:
在MVC架构当中,一般会先从Mapper层,即持久层开始开发。
Mapper层开发
Mapper是Spring提供给我们操作数据库的利器,使用Mapper分为两种情况:
- 涉及到多表查询,需要自定义SQL
- 仅涉及到单表简单查询
先安装MyBatisPlus插件,并设置对应数据库。
随后设置代码生成规则:
选择表:上图选择serve_type表。
设置生成代码的根目录:上图设置generator
设置包路径:上图设置为com.jzo2o.foundations
主键生成策略:根据表中主键的生成策略进行选择,支持的主键生成策略如下图:
勾选要生成的类及包路径(Entity、Mapper、Controller、Service、ServiceImpl)。
勾选是否生成lombok注解、restController注解、swagger注解等。
由于MBP提供的查询是仅支持单表的,所以需要使用多表查询的情况需要自定义Mapper内容。
先定义Mapper接口:
点击左下角图标即可跳转到对应Mapper文件:
Mapper构建完成之后,即可进行单元测试:
Test类通常生成在对应的Test包下:
@SpringBootTest
@Slf4j
class ServeMapperTest {
@Resource
private ServeMapper serveMapper;
@Test
void test_queryServeListByRegionId() {
List<ServeResDTO> serveResDTOS = serveMapper.queryServeListByRegionId(1686303222843662337L);
Assert.notEmpty(serveResDTOS,"列表为空");
}
}
利用断点调试测试Mapper。
Service层
service接口是提供controller调用,service接口的参数由controller传入通常controller方法将它的形参直接传入service方法,对于通用的service方法则需要定义更通用的参数,service方法的返回值类型通常可以直接定义为controller方法的返回值类型。
比如:
public interface IServeService extends IService<Serve> {
/**
* 分页查询服务列表
* @param servePageQueryReqDTO 查询条件
* @return 分页结果
*/
PageResult<ServeResDTO> page(ServePageQueryReqDTO servePageQueryReqDTO);
}
定义实现类:
这里我们使用的Mybatis-Plus提供的ServiceImpl类,如下代码,在此类中有baseMapper,baseMapper即是ServeMapper实例
@Service
public class ServeServiceImpl extends ServiceImpl<ServeMapper, Serve> implements IServeService {
/**
* 分页查询
*
* @param servePageQueryReqDTO 查询条件
* @return 分页结果
*/
@Override
public PageResult<ServeResDTO> page(ServePageQueryReqDTO servePageQueryReqDTO) {
//通过baseMapper调用queryServeListByRegionId方法
}
}
这里有一些关于分页插件的问题需要注意,在用于举例的项目中存在两种分页方法:
使用的是mybatis-plus自带分页方法,通过selectPage方法实现分页
public PageResult<ServeTypeResDTO> page(ServeTypePageQueryReqDTO serveTypePageQueryReqDTO) {
Page<ServeType> page = PageUtils.parsePageQuery(serveTypePageQueryReqDTO, ServeType.class);
Page<ServeType> serveTypePage = baseMapper.selectPage(page, new QueryWrapper<>());
return PageUtils.toPage(serveTypePage, ServeTypeResDTO.class);
}使用com.github.pagehelper分页组件
public PageResult<ServeItemResDTO> page(ServeItemPageQueryReqDTO serveItemPageQueryReqDTO) {
return PageHelperUtils.selectPage(serveItemPageQueryReqDTO,
() -> baseMapper.queryList(serveItemPageQueryReqDTO.getServeTypeId(), serveItemPageQueryReqDTO.getName(), serveItemPageQueryReqDTO.getActiveStatus()));
}
第一种:mybatis-plus自带的分页方法,通过调用selectPage方法实现分页,适用于通过QueryWrapper拼装SQL。 第二种:pagehelper分页组件适用于自定义sql的分页查询。
使用com.github.pagehelper分页插件实现分页功能,下边是它的执行原理:
PageHelperUtils是一个工具类,进入selectPage方法,调用PageHelper.startPage方法设置分页参数,通过一层一层进入源码,最终将分页参数设置到ThreadLocalLOCAL_PAGE=newThreadLocal(); 中。
通过PageInterceptor拦截器拦截 MyBatis的Executor 的 query() 方法得到原始的sql语句,首先得到count总数,然后从newThreadLocal中取出分页参数,在原始sql语句中添加分页参数查询分页数据。
Controller层
在Controller层调用Service提供的方法即可。
前后端联调
经过前面的步骤,一个接口已经完成了99%,接下来的联调决定了接口是否设计成功。
作为后端开发人员,应该使用浏览器提供的开发者工具来跟踪请求,从请求路径分析道响应结果:
至此,一个接口就开发完成了。
Comments NOTHING