【项目记录】准备工作及查询部门
1 开发规范
1.1 前后端分离开发
现在的企业项目开发有2种开发模式:前后台混合开发和前后台分离开发。
前后台混合开发,顾名思义就是前台后台代码混在一起开发
这种开发模式有如下缺点:
1. 沟通成本高:后台人员发现前端有问题,需要找前端人员修改,前端修改成功,再交给后台人员使用
2. 分工不明确:后台开发人员需要开发后台代码,也需要开发部分前端代码。很难培养专业人才
3. 不便管理:所有的代码都在一个工程中
4. 难以维护:前端代码更新,和后台无关,但是需要整个工程包括后台一起重新打包部
所以目前基本都是采用的前后端分离开发方式
将原先的工程分为前端工程和后端工程这2个工程,然后前端工程交给专业的前端人员开发,后端工程交给专业的后端人员开发。
前端页面需要数据,可以通过发送异步请求,从后台工程获取。但是,前后台是分开来开发的,那么前端人员怎么知道后台返回数据的格式呢?后端人员开发,怎么知道前端人员需要的数据格式呢?
所以针对这个问题,前后台统一制定一套规范,前后台开发人员都需要遵循这套规范开发,这就是接口文档。
接口文档有离线版和在线版本,接口文档示可以查询今天提供资料/接口文档里面的资料。
那么接口文档的内容怎么来的呢?是后台开发者根据产品经理提供的产品原型和需求文档所撰写出来的,产品原型示例可以参考今天提供资料/页面原型里面的资料。
那么基于前后台分离开发的模式下,后台开发者开发一个功能的具体流程如何呢?
1. 需求分析:首先需要阅读需求文档,分析需求,理解需求。
2. 接口定义:查询接口文档中关于需求的接口的定义,包括地址,参数,响应数据类型等等
3. 前后台并行开发:各自按照接口文档进行开发,实现需求
4. 测试:前后台开发完了,各自按照接口文档进行测试
5. 前后段联调测试:前段工程请求后端工程,测试功能
1.2 Restful风格
在前后端分离的开发模式中,前后端开发人员都需要根据提前定义好的接口文档,来进行前后端功能的开发。
后端开发人员:必须严格遵守提供的接口文档进行后端功能开发(保障开发的功能可以和前端对接)
什么是REST风格呢?
REST(Representational State Transfer),表述性状态转换,它是一种软件架构风格。
传统URL风格如下:
http://localhost:8080/user/getById?id=1 GET:查询id为1的用户
http://localhost:8080/user/saveUser POST:新增用户
http://localhost:8080/user/updateUser POST:修改用户
http://localhost:8080/user/deleteUser?id=1 GET:删除id为1的用户
原始的传统URL,定义比较复杂,而且将资源的访问行为对外暴露出来了。而且,对于开发人员来说,每一个开发人员都有自己的命名习惯,就拿根据id查询用户信息来说的,不同的开发人员定义的路径可能是这样的:getById,selectById,queryById,loadById... 。 每一个人都有自己的命名习惯,如果都按照各自的习惯来,一个项目组,几十号或上百号人,那最终开发出来的项目,将会变得难以维护,没有一个统一的标准。
基于REST风格URL如下:
http://localhost:8080/users/1 GET:查询id为1的用户
http://localhost:8080/users POST:新增用户
http://localhost:8080/users PUT:修改用户
http://localhost:8080/users/1 DELETE:删除id为1的用户
其中总结起来,就一句话:通过URL定位要操作的资源,通过HTTP动词(请求方式)来描述具体的操作。
在REST风格的URL中,通过四种请求方式,来操作数据的增删改查。
GET : 查询
POST :新增
PUT : 修改
DELETE :删除
我们看到如果是基于REST风格,定义URL,URL将会更加简洁、更加规范、更加优雅。
注:
1. REST是风格,是约定方式,约定不是规定,可以打破
2. 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如:users、emps、books…
1.3 Apifox
上面讲到,该案例中,将会基于Restful风格的接口进行交互,那么其中就涉及到常见的4中请求方式,包括:POST、DELETE、PUT、GET。
因为在浏览器地中所发起的所有的请求,都是GET方式的请求。那大家就需要思考两个问题:
1. 前后端都在并行开发,后端开发完对应的接口之后,如何对接口进行请求测试呢?
2. 前后端都在并行开发,前端开发过程中,如何获取到数据,测试页面的渲染展示呢?
那这里就可以借助一些接口测试工具,比如项:Postman、Apipost、Apifox等。
那这些工具的使用基本类似,只不过Apifox工具的功能更强强大、更加完善,所以采用功能更为强大的Apifox工具。
1.3.1 介绍
介绍:Apifox是一款集成了Api文档、Api调试、Api Mock、Api测试的一体化协作平台。
作用:接口文档管理、接口请求测试、Mock服务。
官网:https://apifox.com/
1.3.2 安装
下载Apifox的安装包
下载后,扫码登陆即可使用
2 工程搭建
2.1 创建SpringBoot工程,并引入web开发起步依赖、mybatis、mysql驱动、lombok。
创建项目
新建一个空项目,取名web-ai-project02
创建新spring boot模块,取名tlias-web-managent,选好语言框架和JDK版本,选择依赖:
2.2 创建数据库及对应的表结构,并在application.yml中配置数据库的基本信息。
创建tlias数据库,并准备dept部门表。
CREATE TABLE dept (id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT 'ID, 主键',name varchar(10) NOT NULL UNIQUE COMMENT '部门名称',create_time datetime DEFAULT NULL COMMENT '创建时间',update_time datetime DEFAULT NULL COMMENT '修改时间'
) COMMENT '部门表';INSERT INTO dept VALUES (1,'学工部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(2,'教研部','2023-09-25 09:47:40','2024-08-09 15:17:04'),(3,'咨询部','2023-09-25 09:47:40','2024-07-30 21:26:24'),(4,'就业部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(5,'人事部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(6,'行政部','2023-11-30 20:56:37','2024-07-30 20:56:37');
在 application.yml 配置文件中配置数据库的连接信息。
#配置数据库连接信息
spring:application:name: tlias-web-managementdatasource:url: jdbc:mysql://localhost:3306/tliasdriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: '050516'mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.3 准备基础包结构,并引入实体类Dept及统一的响应结果封装类Result
准备基础包结构
实体类Dept
package org.example.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {private Integer id;private String name;private LocalDateTime createTime;private LocalDateTime updateTime;
}
统一响应结果result
package com.itheima.pojo;import lombok.Data;
import java.io.Serializable;/*** 后端统一返回结果*/
@Data
public class Result {private Integer code; //编码:1成功,0为失败private String msg; //错误信息private Object data; //数据public static Result success() {Result result = new Result();result.code = 1;result.msg = "success";return result;}public static Result success(Object object) {Result result = new Result();result.data = object;result.code = 1;result.msg = "success";return result;}public static Result error(String msg) {Result result = new Result();result.msg = msg;result.code = 0;return result;}}
基础代码结构:
DeptMapper接口
package org.example.mapper;import org.apache.ibatis.annotations.Mapper;@Mapper
public interface DeptMapper {
}
DeptService接口
package org.example.service;public interface DeptService {
}
DeptServiceImpl类
package org.example.service.impl;import org.example.service.DeptService;
import org.springframework.stereotype.Service;@Service
public class DeptServiceImpl implements DeptService {
}
DeptController类
package org.example.controller;import org.springframework.web.bind.annotation.RestController;/*** 部门管理控制器*/
@RestController
public class DeptController {
}
3 部门查询
3.1 基本实现
3.1.1 需求
查询所有的部门数据,查询出来展示在部门管理的页面中。
3.1.2 接口描述
(1)基本信息
请求路径:depts
请求方式:GET
接口描述:该接口用于部门列表数据查询
(2)请求参数
无
(3)响应数据
参数格式:application/json
参数说明:
参数名 | 类型 | 是否必须 | 备注 |
---|---|---|---|
code | number | 必须 | 响应码,1 代表成功,0 代表失败 |
msg | string | 非必须 | 提示信息 |
data | object[ ] | 非必须 | 返回的数据 |
|- id | number | 非必须 | id |
|- name | string | 非必须 | 部门名称 |
|- createTime | string | 非必须 | 创建时间 |
|- updateTime | string | 非必须 | 修改时间 |
响应样例:
{"code": 1,"msg": "success","data": [{"id": 1,"name": "学工部","createTime": "2022-09-01T23:06:29","updateTime": "2022-09-01T23:06:29"},{"id": 2,"name": "教研部","createTime": "2022-09-01T23:06:29","updateTime": "2022-09-01T23:06:29"}]
}
3.1.3 实现思路
明确了删除部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:
Controller层,负责接收前端发起的请求,并调用service查询部门数据,然后响应结果。
Service层,负责调用Mapper接口方法,查询所有部门数据。
Mapper层,执行查询所有部门数据的操作。
3.1.4 代码实现
1) Controller层
在 DeptController 中,增加 list 方法,代码如下:
/*** 部门管理控制器*/
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*/@RequestMapping("/depts")public Result list(){List<Dept> deptList = deptService.findAll();return Result.success(deptList);}
}
2) Service层
在 DeptService 中,增加 findAll方法,代码如下:
public interface DeptService {/*** 查询所有部门*/public List<Dept> findAll();
}
在 DeptServiceImpl 中,增加 findAll方法,代码如下:
@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptMapper deptMapper;public List<Dept> findAll() {return deptMapper.findAll();}
}
3) Mapper层
在 DeptMapper 中,增加 findAll方法,代码如下:
@Mapper
public interface DeptMapper {/*** 查询所有部门*/@Select("select * from dept")public List<Dept> findAll();}
3.1.5 接口测试
启动项目,然后就可以打开Apifox进行测试了。
我们发现,已经查询出了所有的部门数据,并且响应回来的就是json格式的数据,与接口文档一致。 那接下来再来测试一下这个查询操作使用post、put、delete方式来请求,是否可以获取到数据。
经过测试发现,其实是可以通过任何方式的请求来访问查询部门的这个接口的。 而在接口文档中,明确要求该接口的请求方式为GET,那么如何限制请求方式呢?
方式一:在controller方法的@RequestMapping注解中通过method属性来限定。
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*/@RequestMapping(value = "/depts", method = RequestMethod.GET)public Result list(){List<Dept> deptList = deptService.findAll();return Result.success(deptList);}
}
方式二:在controller方法上使用,@RequestMapping的衍生注解 @GetMapping。 该注解就是标识当前方法,必须以GET方式请求。
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*/@GetMapping("/depts")public Result list(){List<Dept> deptList = deptService.findAll();return Result.success(deptList);}
}
上述两种方式,在项目开发中,推荐使用第二种方式,简洁、优雅。
GET方式:@GetMapping
POST方式:@PostMapping
PUT方式:@PutMapping
DELETE方式:@DeleteMapping
3.1.6 数据封装
在上述测试中会发现部门的数据中,id、name两个属性是有值的,但是createTime、updateTime两个字段值并未成功封装,而数据库中是有对应的字段值的,这是为什么呢?
原因如下:
实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。
解决方案:
手动结果映射
起别名
开启驼峰命名
1). 手动结果映射
在DeptMapper接口方法上,通过 @Results及@Result 进行手动结果映射。
@Results({@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")})
@Select("select id, name, create_time, update_time from dept")
public List<Dept> findAll();
2). 起别名
在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。
@Select("select id, name, create_time createTime, update_time updateTime from dept")
public List<Dept> findAll();
3). 开启驼峰命名(推荐)
如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。驼峰命名规则: abc_xyz => abcXyz
表中字段名:abc_xyz
类中属性名:abcXyz
在application.yml中做如下配置,开启开关。
mybatis:configuration:map-underscore-to-camel-case: true
注:要使用驼峰命名的前提是 实体类的属性 与 数据库表中的字段名严格遵守驼峰命名。
3.2 前后端联调
3.2.1 联调测试
完成了查询部门的功能,Apifox 工具测试也通过了,下面再基于前后端分离的方式进行接口联调。具体操作如下:
1) 将压缩包,拷贝到一个没有中文不带空格的目录下。
https://pan.baidu.com/s/1vWRtv2qm5QSNYbxwwHfwzg?pwd=k8xf
2) 解压(解压到当前目录)
3) 双击 nginx.exe 启动Nginx,一闪而过,就说明nginx已启动完成,或者进入任务管理器,点击详细信息,查询nginx,如果有,则已成功运行。
4) 打开浏览器,访问:http://localhost:90
3.2.2 请求访问流程
前端工程请求服务器的地址为http://localhost:90/api/depts,那又是如何访问到后端的tomcat服务器的?
其实这里是通过前端服务Nginx中提供的反向代理功能实现的。
1) 浏览器发起请求,请求的是localhost:90 ,那其实请求的是nginx服务器。
2) 在nginx服务器中呢,并没有对请求直接进行处理,而是将请求转发给了后端的tomcat服务器,最终由tomcat服务器来处理该请求。
这个过程就是通过nginx的反向代理功能实现的。 那为什么浏览器不直接请求后端的tomcat服务器,而是直接请求nginx服务器呢?主要有以下几点原因:
1) 安全:由于后端的tomcat服务器一般都会搭建集群,会有很多的服务器,把所有的tomcat暴露给前端,让前端直接请求tomcat,对于后端服务器是比较危险的。
2) 灵活:基于nginx的反向代理实现,更加灵活,后端想增加、减少服务器,对于前端来说是无感知的,只需要在nginx中配置即可。
3) 负载均衡:基于nginx的反向代理,可以很方便的实现后端tomcat的负载均衡操作。
具体的请求访问流程如下:
1. location:用于定义匹配特定url请求的规则。
2. ^~ /api/:表示精确匹配,即只匹配以/api/开头的路径。
3. rewrite:该指令用于重写匹配到的uri路径。
4. proxy_pass:该指令用于代理转发,它将匹配到的请求转发给位于后端的指令服务器。