一.资料
1.完整项目:
2.测试工具-postman
3.WEB服务器-tomcat-10.1.18
4.java第三方库
Central Repository: (maven.org)
5.IDEA
6.百度网盘资料
7.biliblili
二.准备
1.tomcat安装与部署:
b站:p56-p59
下载
1.百度网盘
资料下面的software目录下有tomcat安装包,同时含有postman,vscode等安装包
2.通过上面的链接下载,版本有所差异
安装和配置
-正确安装JDK并配置JAVA_HOME(以JDK17为例 https://injdk.cn中可以下载各种版本的JDK)
-解压tomcat到非中文无空格目录
-点击bin/startup.bat启动
-控制台出现乱码?
第4步给出答案
-打开浏览器输入 http://localhost:8080访问测试,出现以下画面说明tomcat配置成功
-直接关闭窗口或者运行 bin/shutdown.bat关闭tomcat
2.IDEA关联并部署tomcat
b站:p60-p61
3.Web项目搭建
p62
2,3后续单独更一期
4.乱码问题
b站:p85-p88
1.查看输出日志控制台的编码格式
2.进入tomcat目录下的config文件
3.找到logging.properties文件,里面可以设置日志的编码格式
5.如何使用postman?
b站: p184
下载注册后进入postman首页
1.创建一个存放测试数据的文件
点击"+"号,点击Blank collection,设置文件名称即可
2.点击“+”号,就可以发送请求了
3.样例
二.功能简介
-
用户功能
-
注册功能
-
登录功能
-
-
头条新闻
-
新闻的分页浏览
-
通过标题关键字搜索新闻
-
查看新闻详情
-
新闻的修改和删除
-
-
权限控制
-
用户只能修改和自己发布的头条新闻
-
三.数据库设计
mysql-8.0.32
news_headline 新闻详细信息表
hid int -新闻id -主键
title varchar -标题
article varchar-内容
publisher int -作者
page_views int -浏览量
type int -新闻类型id
create_time datetime -发布时间
update_time datetime-更新时间
is_deleted int -是否被删除
news_type - 新闻类型表
tid int -新闻类型id -主键
tname varchar -新闻类型名称
news_user -用户表
uid int -用户id -主键
username varchar -用户名
user_pwd varchar 密码
nick_name varchar 昵称
四.后端项目结构
- src
-
util 工具类包,主要存放一些工具类
-
MD5Util 加密工具类
-
JDBCUtil连接池工具类
-
JwtHelper 处理token相关信息
-
WebUtil 处理json数据
-
-
test 测试代码
-
service 服务层, 主要用于处理业务逻辑
-
Impl 实现类
-
NewsUserServiceImpl
-
NewsHeadlineServiceImpl
-
NewsTypeServiceImpl
-
-
interface NewsUserService
-
interface NewsHeadlineService
-
interface NewsTypeService
-
-
pojo 实体类层 ,存放实体类
-
package-vo
-
HeadlineQueryVo 带条件查询,关键字封装
-
HeadlinePageVo 封装响应给客户端的数据
-
HeadlineDetail 显示新闻详情
-
-
NewsUser
-
NewsHeadline
-
NewsType
-
-
filters 过滤器包, 存放过滤器代码
-
CrosFilter 跨域过滤器,解决跨域问题
-
LoginFilter 检验登陆状态
-
-
dao 数据访问层,主要用于定义对各个表格的curd方法
-
impl 存放实现类
-
NewsUserDaoImpl
-
NewsTypeDaoImpl
-
NewsHeadlineDaoImpl
-
-
BaseDao 操作数据库
-
interface NewsUserDao
-
interface NewsTypeDao
-
interface NewsHeadlineDao
-
-
controller 控制层代码,主要由Servlet组成
-
BaseControl 使用不同方法控制不同请求
-
NewsHeadlineControl 管理新闻
-
NewsUserControler 管理用户
-
NewsTypeControler 管理新闻类型
-
ProptalController 门户控制器,未登录状态下访问信息
-
-
common 公共包
-
Result 异步响应格式类
-
ResultCodeEnum 枚举类,枚举后端响应状态
-
-
resources
-
jdbc.properties 数据库连接相关配置信息
-
-
五.请求参数获取及响应
主要展示请求信息,代码细节 在 百度网盘 或 github中 08_第八章 微头条项目开发.md 文件
(零) util&comman类
JDBCUtil
-BaseDao类中用到,连接数据库
--Connection connection = JDBCUtil.getConnection();
JwtHelper
-登录成功或生成token,NewsUserControl类中login方法用到
--String JwtHelper.createToken(Long uid) 创建token
--Long JwtHelper.getUserId(String token) 通过token获取uid
--boolean JwtHelper.isExpiration() token 是否过期=过期?ture:false
MD5Util
-加密用户密码
--String MD5Util.encrypt(String userPwd)
WEBUtil
-读取JSON串
--WEBUtil.readJson()
--WEBUtil.WriteJson()
(一)登录
1.点击登录后
客户端
发送请求: uri:user/login
请求方式:
POST
请求头:
无
请求体:
JSON格式
响应示例:
登录成功
{ "code":"200", // 成功状态码 "message":"success" // 成功状态描述 "data":{ "token":"... ..." // 用户id的token } }
用户名有误
{ "code":"501", "message":"用户名有误" "data":{} }
密码有误
{ "code":"503", "message":"密码有误" "data":{} }
后端
@WebServlet("/user/*") //通过注解配置映射路径,表示在user下进行模糊匹配 public class NewsUserControl extends BaseController{ //BaseController 中重写了service方法,通过反射获取uri中的路径最后一位并调用该方法 //例如:uri = user/login,BaseController则获取login,通过反射调用NewsUserControl //下的login方法 //调用NewsUserService接口完成参数处理工作 private NewsUserService newsUserService= new NewsUserServiceImpl(); protected void login(HttpServletRequest req, HttpServletResponse resp) { //接收JSON数据 -调用WEBUtil方法的readJson获取NewsUser对象 NewsUser newsUser = WEBUtil.readJson(req, NewsUser.class); //登录验证 //创建Result对象 //密码使用MD5Util.entry()加密 //判断账号和密码 //如果登录成功将通过JwtHelp生成token存入map中 {"data":token} //将结果以map形式封装到Result对象 Result result = null; //判断用户名 if(loginUser != null) { //判断密码 if(loginUser.getUserPwd().equals(MD5Util.encrypt(newsUser.getUserPwd()))) { //密码正确 Map<String,Object> data = new HashMap<String,Object>(); //生成token String token = JwtHelper.createToken(loginUser.getUid().longValue()); //封装结果 data.put("token", token); result = Result.ok(data); }else{ //密码错误 result = Result.build(null, ResultCodeEnum.PASSWORD_ERROR); } }else{ // 封装用户名错误结果 result=Result.build(null, ResultCodeEnum.USERNAME_ERROR); } //响应结果JSON格式 //通过WEBUtil.writeJson()将生成的token发送到前端 //响应结果 WEBUtil.writeJson(resp,result); } }
2.登录成功后
客户端
发送请求: uri:user/getUserInfo
请求方式:
GET
请求头:
token:......
请求体:
无
响应示例
成功获取
{ "code": 200, "message": "success", "data": { "loginUser": { "uid": 1, "username": "zhangsan", "userPwd": "", "nickName": "张三" } } }
获取失败
{ "code": 504, "message": "notLogin", "data": null }
后端
@WebServlet("/user/*") //通过注解配置映射路径,表示在user下进行模糊匹配 public class NewsUserControl extends BaseController{ protected void getUserInfo(HttpServletRequest req, HttpServletResponse resp) { //String token = req.getParameter("token"); //从请求头中获取用户信息,而不是请求体 String token = req.getHeader("token"); if(null != token){ if(!JwtHelper.isExpiration(token)){ Integer uid = JwtHelper.getUserId(token).intValue(); //token没过期且不为空 NewsUser newsUser = newsUserService.findByUserId(uid); //获取用户信息 newsUser.setUserPwd(""); Map<String,NewsUser> data = new HashMap<>(); data.put("loginUser",newsUser); result = Result.ok(data); } } WEBUtil.writeJson(resp,result); } }
(二)注册
1.注册时用户名占用校验
客户端
发送请求: uri:user/checkUserName
请求方式:
POST
请求头:
无
请求体:
键值对
响应示例
用户名校验通过
{ "code":"200", "message":"success" "data":{} }
用户名占用
{ "code":"505", "message":"用户名占用" "data":{}
后端
@WebServlet("/user/*") //通过注解配置映射路径,表示在user下进行模糊匹配 public class NewsUserControl extends BaseController{ protected void checkUserName(HttpServletRequest req, HttpServletResponse resp) { //前端传来的是键值对,则可以通过getParameter(key)的方式获取数据 String username = req.getParameter("username"); NewsUser newsUser = newsUserService.findByUserName(username); Result result=null; if (null == newsUser){ //用户名未被占用 result=Result.ok(null); }else{ result=Result.build(null,ResultCodeEnum.USERNAME_USED); } WEBUtil.writeJson(resp,result); } }
(二)注册表单提交
客户端
发送请求: uri:user/regist
请求方式:
POST
请求头:
无
请求体:
JSON
响应示例
注册成功
{ "code":"200", "message":"success" "data":{} }
用户名占用
{ "code":"505", "message":"用户名占用" "data":{} }
后端
@WebServlet("/user/*") //通过注解配置映射路径,表示在user下进行模糊匹配 public class NewsUserControl extends BaseController{ protected void regist(HttpServletRequest req, HttpServletResponse resp) { NewsUser register = WEBUtil.readJson(req, NewsUser.class); //校验用户名是否被占用 NewsUser newsUser = newsUserService.findByUserName(register.getUsername()); Result result = null; if(newsUser == null){ //用户名未被占用 newsUserService.registUser(register); result = Result.ok(null); }else{ result = Result.build(null,ResultCodeEnum.USERNAME_USED); } WEBUtil.writeJson(resp, result); } }
(三)微头条首页
客户端
发送请求: uri:portal/findAllTypes
请求方式:
GET
请求头:
无
请求体:
无
响应示例
List<NewsType>
{
"code":"200",
"message":"OK"
"data:
[
{
"tid":"1",
"tname":"新闻"
},
{
"tid":"2",
"tname":"体育"
},
{
"tid":"3",
"tname":"娱乐"
},
{
"tid":"4",
"tname":"科技"
},
{
"tid":"5",
"tname":"其他"
}
]
}
后端
@WebServlet("/portal/*") public class PortalController extends BaseController { protected void findAllTypes(HttpServletRequest req, HttpServletResponse resp) { List<NewsType> newsTypeList = newsTypeService.findAll(); WEBUtil.writeJson(resp, Result.ok(newsTypeList)); } }
(四)分页带条件查询所有头条
客户端
发送请求: uri:portal/findAllTypes
请求方式:
GET
请求头:
无
请求体:
无
响应示例
{ "code":"200", "message":"success" "data":{ "pageInfo":{ "pageData":[ // 本页的数据 { "hid":"1", // 新闻id "title":"尚硅谷宣布 ... ...", // 新闻标题 "type":"1", // 新闻所属类别编号 "pageViews":"40", // 新闻浏览量 "pastHours":"3" , // 发布时间已过小时数 "publisher":"1" // 发布用户ID }, { "hid":"1", // 新闻id "title":"尚硅谷宣布 ... ...", // 新闻标题 "type":"1", // 新闻所属类别编号 "pageViews":"40", // 新闻浏览量 "pastHours":"3", // 发布时间已过小时数 "publisher":"1" // 发布用户ID }, { "hid":"1", // 新闻id "title":"尚硅谷宣布 ... ...", // 新闻标题 "type":"1", // 新闻所属类别编号 "pageViews":"40", // 新闻浏览量 "pastHours":"3", // 发布时间已过小时数 "publisher":"1" // 发布用户ID } ], "pageNum":1, //页码数 "pageSize":10, // 页大小 "totalPage":20, // 总页数 "totalSize":200 // 总记录数 } } }
后端
@WebServlet("/portal/*") public class PortalController extends BaseController { protected void findNewsPage(HttpServletRequest req, HttpServletResponse resp) { //读取数据 HeadlineQueryVo headLineQueryVo = WEBUtil.readJson(req, HeadlineQueryVo.class); Map<String,Object> pageInfo = newsHeadlineService.findPage(headLineQueryVo); Map<String,Object> pageInfoMap = new HashMap<String,Object>(); pageInfoMap.put("pageInfo", pageInfo); //返回数据 //pageInfo 的类型为 List<HeadlinePageVo> //{"pageInfo":{pageData,"pageNum","pageSize","totalPage","totalSize"}} WEBUtil.writeJson(resp,Result.ok(pageInfoMap)); } }
(五)查看头条详情
客户端
发送请求: uri:portal/HeadlineDetail
请求方式:
POST
请求头:
无
请求体:
键值对
响应示例
{ "code":"200", "message":"success", "data":{ "headline":{ "hid":"1", // 新闻id "title":"马斯克宣布 ... ...", // 新闻标题 "article":"... ..." // 新闻正文 "type":"1", // 新闻所属类别编号 "typeName":"科技", // 新闻所属类别 "pageViews":"40", // 新闻浏览量 "pastHours":"3" , // 发布时间已过小时数 "publisher":"1" , // 发布用户ID "author":"张三" // 新闻作者 } } }
后端
@WebServlet("/portal/*") public class PortalController extends BaseController { protected void showHeadlineDetail(HttpServletRequest req, HttpServletResponse resp) { //注释消失了(狗头),具体见资料(上面) Integer hid = Integer.parseInt(req.getParameter("hid")); Result result = null; HeadlineDetailVo headline = newsHeadlineService.findHeadlineDetail(hid); Map<String, Object> headlineMap = new HashMap<>(); headlineMap.put("headline", headline); result = Result.ok(headlineMap); WEBUtil.writeJson(resp, result); } }
(五)头条的修改和删除
对新闻进行操作前查看登录信息是否有效
1.登录校验
客户端
发送请求: uri:user/checkLogin
请求方式:
GET
请求头:
token
请求体:
无
响应示例
登录未过期
{ "code":"200", "message":"success", "data":{} }
登录已过期
{ "code":"504", "message":"loginExpired", "data":{} }
后端
@WebServlet("/user/*") public class NewsUserControl extends BaseController { protected void checkLogin(HttpServletRequest req, HttpServletResponse resp) { //注意 请求参数 getParameter //请求头 getHeader String token = req.getHeader("token"); Result result = null; if(!JwtHelper.isExpiration(token)){ //token未过期 result = Result.ok(null); }else{ result = Result.build(null,ResultCodeEnum.NOTLOGIN); } WEBUtil.writeJson(resp,result); } }
2.提交头条发布
客户端
发送请求: uri:headline/publish
请求方式:
POST
请求头:
token
请求体:
JSON
响应示例
发布成功
{ "code":"200", "message":"success", "data":{} }
失去登录状态发布失败
{ "code":"504", "message":"loginExpired", "data":{} }
后端
@WebServlet("/headline/*") public class NewsHeadlineControl extends BaseController { private NewsHeadlineService headlineService = new NewsHeadlineServiceImpl(); protected void publish(HttpServletRequest req, HttpServletResponse resp) { String token = req.getHeader("token"); //通过token获取用户id Long userId = JwtHelper.getUserId(token); HeadlineDetailVo headlineDetailVo = WEBUtil.readJson(req, HeadlineDetailVo.class); headlineDetailVo.setPublisher(userId.intValue()); //将新闻信息存入数据库 headlineService.addNewsHeadline(headlineDetailVo); WEBUtil.writeJson(resp,Result.ok(null)); } }
3.修改头条回显
点击修改头条时,显示原来内容
客户端
发送请求: uri:headline/findHeadlineByHid
请求方式:
POST
请求头:
无
请求体:
键值对
响应示例
查询成功
{ "code":"200", "message":"success", "data":{ "headline":{ "hid":"1", "title":"马斯克宣布", "article":"... ... ", "type":"2" } } }
后端
@WebServlet("/headline/*") public class NewsHeadlineControl extends BaseController { private NewsHeadlineService headlineService = new NewsHeadlineServiceImpl(); protected void findHeadlineByHid(HttpServletRequest req, HttpServletResponse resp) { Integer hid = Integer.parseInt(req.getParameter("hid")); //通过新闻id获取对应新闻内容 HeadlineDetailVo headlineDetailVo = headlineService.getByHid(hid); Map<String, Object> headlineMap = new HashMap<>(); headlineMap.put("headline", headlineDetailVo); WEBUtil.writeJson(resp,Result.ok(headlineMap)); } }
4.保存修改
客户端
发送请求: uri:headline/update
请求方式:
POST
请求头:
无
请求体:
键值对
响应示例
修改成功
{ "code":"200", "message":"success", "data":{} }
修改失败
{ "code":"504", "message":"loginExpired", "data":{} }
后端
@WebServlet("/headline/*") public class NewsHeadlineControl extends BaseController { private NewsHeadlineService headlineService = new NewsHeadlineServiceImpl(); protected void update(HttpServletRequest req, HttpServletResponse resp) { HeadlineDetailVo headlineDetailVo = WEBUtil.readJson(req,HeadlineDetailVo.class); //将新闻修改内容更新到数据库 headlineService.updateHeadline(headlineDetailVo); WEBUtil.writeJson(resp,Result.ok(null)); } }
5.删除头条
客户端
发送请求: uri:headline/removeByHid
请求方式:
POST
请求头:
无
请求体:
键值对
响应示例
删除成功
{ "code":"200", "message":"success", "data":{} }
删除失败
{ "code":"504", "message":"loginExpired", "data":{} }
后端
@WebServlet("/headline/*") public class NewsHeadlineControl extends BaseController { private NewsHeadlineService headlineService = new NewsHeadlineServiceImpl(); protected void removeByHid(HttpServletRequest req, HttpServletResponse resp) { Integer hid = Integer.parseInt(req.getParameter("hid")); headlineService.removeByHid(hid); WEBUtil.writeJson(resp,Result.ok(null)); } }
六.案例中遇到的问题
-
tomcat输出日志乱码问题
查看idea或cmd编码格式,到tomcat目录下,找到conf目录下的logging.properties,修改对应编码格式即可
-
请求参数读取出错
json数据错误使用getParameter()读取
-
应使用ObjectMapper方法读取
请求头数据错误使用getParameter()读取
-
应使用getHeader()方法读取
-
-
访问路径出错
@WebServlet("/headline"),导致headline路径下方法不能被获得请求
-
应修改为@WebServlet("/headline/*")
-
-
数据库操作失误
数据库采用下划线分割命名,而java中采用驼峰式命名
-
访问时需要注意起别名,变量应转换为与java一致
操作数据库时注意sql语句空格、变量顺序、数据类型等问题
-
其他的,就想不起来了......
本文侧重前后端统一接口的交互,其余代码没放出来,需要自取,菜鸡一枚,有错误和不足的地方还望指出。