目录
一、编写注册功能
🍅 1、使用ajax构造请求(前端)
🍅 2、统一处理
🎄 统一对象处理
🎄 保底统一返回处理
🎄 统一异常处理
🍅 3、处理请求
二、编写登录功能
🍅 1、使用ajax构造请求
🍅 2、处理请求
🎄 创建实体类
🎄 添加接口方法
🎄 service层
🎄 controller:处理请求并返回
三、编写博客列表页面
🍅 1、SessionUtils
🍅 2、处理请求
🍅 3、使用ajax构造请求
三、编写注销功能
🍅 1、使用ajax获取请求
🍅 2、处理请求
四、编写删除文章功能
🍅 1、使用ajax构造请求
🍅 2、处理请求
🎄 mapper层:接口方法
🎄 service层
🎄 controller层:处理请求
五、编写文章添加功能
🍅 1、使用ajax构造请求
🍅 2、处理请求
🎄mapper层:添加接口方法
🎄service层:返回受影响行数
🎄 controller:处理添加文章的请求
六、修改功能:查询到修改的文章
🍅 1、通过ajax构造请求
🍅 2、处理请求
🎄Mapper层:添加接口
🎄service层
🎄controller层
七、修改功能:修改查询到的文章
🍅 1、通过ajax构造请求
🍅 2、处理请求
🎄 mapper层
🎄 service层
🎄 controller层
八、文章详情
🍅 1、前端页面编写
🎄 给定id值
🎄 使用ajax构造请求
🍅 2、处理请求
🎄 mapper层
🎄 service层
🎄 controller层
九、阅读量设置
🍅 处理请求
🎄 mapper层
🎄 service层
🎄 controller层
十、分页处理
🍅 1、前端处理
🍅 2、后端处理
🎄 mapper层
🎄 service层
🎄 controller层
十一、密码加盐
🍅 1、定义加密规则
🍅 2、修改UserController代码
十二、session升级存储到redis
🍅 1、先添加redis相关依赖
🍅 2、添加redis配置信息(properties)
十三、实现自定义拦截器
前置知识:
前后端交互的关键:使用AJAX(异步局部提交)
写法:
1、原生写法(兼容性差)
2、使用框架,jQuery ajax(简单 / 通用性浩)
语法:
jQuery.ajax({ url:"接口地址", //提高的接口地址 type:"GET", //请求的类型 data:{ //传递的数据 "username":"张三", "password":"123" }, success:function(res){ //相应的结果是什么 //后端返回数据后的业务处理代码 } });
一、编写注册功能
🍅 1、使用ajax构造请求(前端)
在前端页面的<head></head>添加jquery
<script src="js/jquery.min.js"></script>
添加事件:点击提交按钮,触发mysub()
<div class="row">
<button id="submit" onclick="mysub()">提交</button>
</div>
在<script></script>中提交用户注册信息
主要有这几步:
(1)参数效验(获取到数据|非空效验)
(2)将数据提交给后端
(3)将后端返回的结果给用户
username.focus():指将光标返回
trim():去空格操作(校验到全是空格)
<script>
// 提交用户注册信息
function mysub(){
// 参数校验
var username = jQuery("#username");
var password = jQuery("#password");
var password2 = jQuery("#password2");
if(username.val().trim()==""){
alert("请先输入用户名!");
username.focus();
return false;
}
if(password.val().trim()==""){
alert("请先输入密码!");
password.focus();
return false;
}
if(password2.val().trim()==""){
alert("请先输入确认密码!");
password2.focus();
return false;
}
// 效验两次输入的密码是否一致
if(password.val()!=password2.val()){
alert("两次密码不一致,请先检查!");
return false;
}
// 提交数据给后端
jQuery.ajax({
url:"/user/reg",
// 查询使用get,非查询使用post
type:"POST",
data:{
"username":username.val().trim(),
"password":password.val().trim()
},
success:function (res){
// 返回结果给用户
if (res.code==200 && res.data==1){
alert("注册成功!")
location.href = "login.html";
}else {
alert("注册失败!" + res.msg);
}
}
});
}
</script>
</body>
🍅 2、统一处理
🎄 统一对象处理
在common包中,编写ResultAjax,表示前后端交互的统一对象,统一处理
@Data
public class ResultAjax {
private int code; //状态码
private String msg; //状态码的描述信息
private Object data;
public static ResultAjax success(Object data){
ResultAjax result = new ResultAjax();
result.setCode(200);
result.setMsg("");
result.setData(data);
return result;
}
public static ResultAjax success(int code,String msg,Object data){
ResultAjax result = new ResultAjax();
result.setCode(code);
result.setMsg(msg);
result.setData(data);
return result;
}
public static ResultAjax fail(int code,String msg){
ResultAjax result = new ResultAjax();
result.setCode(code);
result.setMsg(msg);
result.setData(null);
return result;
}
public static ResultAjax fail(int code,String msg,Object data){
ResultAjax result = new ResultAjax();
result.setCode(code);
result.setMsg(msg);
result.setData(data);
return result;
}
}
🎄 保底统一返回处理
在common中创建类
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof ResultAjax){
return body;
}
if (body instanceof String){
ResultAjax resultAjax = ResultAjax.success(body);
try {
return objectMapper.writeValueAsString(resultAjax);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return ResultAjax.success(body);
}
}
🎄 统一异常处理
/*
* 统一异常处理
* */
@RestControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public ResultAjax doException(Exception e){
return ResultAjax.fail(-1,e.getMessage());
}
}
🍅 3、处理请求
编写Mapper层,将拿到的数据进行插入
@Mapper
public interface UserMapper {
@Insert("insert into userinfo(username,password) values (#{username},#{password})")
int reg(Userinfo userinfo);
}
然后在Service层中使用@Autowired将userMapper注入
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public int reg(Userinfo userinfo){
return userMapper.reg(userinfo);
}
}
在UserController中使用Userinfo对象接收请求信息
(1)校验参数
(2)请求 service 进行添加操作
(3)将执行的结果返回给前端
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/reg")
public ResultAjax reg(Userinfo userinfo){
if (userinfo == null || !StringUtils.hasLength(userinfo.getUsername())
|| !StringUtils.hasLength(userinfo.getPassword())){
return ResultAjax.fail(-1,"非法参数");
}
int result = userService.reg(userinfo);
return ResultAjax.success(result);
}
检验该功能是否实现
查看数据库是否插入张三的信息:
二、编写登录功能
🍅 1、使用ajax构造请求
在前端页面的<head></head>添加jquery
<script src="js/jquery.min.js"></script>
添加事件:点击提交按钮,触发mysub()
<div class="row">
<button id="submit" onclick="doLogin()">提交</button>
</div>
(1)校验参数
(2)将数据提交给后端
(3)将结果展示给前端
<script>
function doLogin(){
// 参数校验
var username = jQuery("#username");
var password = jQuery("#password");
if(username.val().trim()==""){
alert("请先输入用户名!");
username.focus();
return false;
}
if(password.val().trim()==""){
alert("请先输入密码!");
password.focus();
return false;
}
// 将数据提交给后端
jQuery.ajax({
url:"/user/login",
type:"GET",
data:{
"username":username.val(),
"password":password.val()
},
success:function(res){
// 将结果展示给用户
if (res.code==200 && res.data==1){
alert("登录成功!");
location.href="myblog_list.html";
}else {
alert("登录失败!" + res.msg);
}
}
});
}
</script>
🍅 2、处理请求
🎄 创建实体类
首先,再创建一个扩展Userinfo的实体类,UserinfoVO
/*
* userinfo扩展类
* */
@Data
public class UserinfoVO extends Userinfo {
private String checkCode;
}
🎄 添加接口方法
在UserMapper中添加以下方法:
@Select("select * from userinfo where username = #{username} order by id desc")
Userinfo getUserByName(@Param("username")String username);
🎄 service层
public Userinfo getUserByName(String username){
return userMapper.getUserByName(username);
}
🎄 controller:处理请求并返回
首先创建一个公共类(common包中):AppVariable
设置一个用户的session key
/*
* 全局变量
* */
public class AppVariable {
public static final String SESSION_USERINFO_KEY = "SESSION_USERINFO";
}
(1)参数校验
(2)根据用户名查询对象
(3)适用对象中的密码和用户输入的密码进行比较
登录验证思路:根据用户名查询对象,如果查询到对象,则用查询到的对象和传递过 来的密码对比。如果相同,则说明登陆成功,反之则不能。
(4)比较成功之后,将对象存储到session中
(5)将结果返回给用户
@RequestMapping("/login")
public ResultAjax login(UserinfoVO userinfoVO, HttpServletRequest request){
//参数校验
if (userinfoVO == null || !StringUtils.hasLength(userinfoVO.getUsername()) ||
!StringUtils.hasLength(userinfoVO.getPassword())){
// 是非法登录
return ResultAjax.fail(-1,"参数有误!");
}
//根据用户查询对象
Userinfo userinfo = userService.getUserByName(userinfoVO.getUsername());
if (userinfo == null || userinfo.getId() == 0){
return ResultAjax.fail(-2,"用户名或密码错误!");
}
//使用对象中的密码和用户输入的密码进行比较
if (!userinfoVO.getPassword().equals(userinfo.getPassword())){
// 密码错误
return ResultAjax.fail(-2,"用户名或密码错误!");
}
//比较成功,将对象存储到session中
HttpSession session = request.getSession();
session.setAttribute(AppVariable.SESSION_USERINFO_KEY,userinfo);
//将结果返回给用户
return ResultAjax.success(1);
}
三、编写博客列表页面
🍅 1、SessionUtils
由于要拿到用户的信息,也就是拿到用户对象,比如修改和删除博客等功能(都是需要拿到用户),所以得到用户对象就属于一个高频事件。
所以就创建一个类,可以得到用户对象。
/*
* session工具类
* */
public class SessionUtils {
// 得到登录用户
public static Userinfo getUser(HttpServletRequest request){
// false代表有session就创建session,没有就创建
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute(AppVariable.SESSION_USERINFO_KEY) != null){
// 登录状态
return (Userinfo) session.getAttribute(AppVariable.SESSION_USERINFO_KEY);
}
return null;
}
}
🍅 2、处理请求
🎄 添加接口方法(mapper)
@Mapper
public interface ArticleMapper {
@Select("select * from articleinfo where uid=#{uid}")
List<ArticleMapper> getListByUid(@Param("uid")int uid);
}
🎄 service层
对象注入
@Service
public class ArticleService {
@Autowired
private ArticleMapper articleMapper;
public List<ArticleMapper> getListByUid(int uid){
return articleMapper.getListByUid(uid);
}
}
🎄controller处理请求并返回响应
使用并行的方式处理文章正文(_DESC_LENGTH是简介)
@RestController
@RequestMapping("art")
public class ArticleController {
@Autowired
public ArticleService articleService;
private static final int _DESC_LENGTH = 120;
@RequestMapping("/mylist")
public ResultAjax myList(HttpServletRequest request){
// 得到登录用户
Userinfo userinfo = SessionUtils.getUser(request);
if (userinfo == null){
return ResultAjax.fail(-1,"请先登录");
}
// 根据用户id查询用户发表的文章
List<Articleinfo> list = articleService.getListByUid(userinfo.getId());
// 处理list -> 将文章正文变成简介
if (list != null && list.size() > 0){
// 并行处理list集合
list.stream().parallel().forEach((art)->{
if (art.getContent().length() > _DESC_LENGTH){
art.setContent(art.getContent().substring(0,_DESC_LENGTH));
}
});
}
// 返回前端
return ResultAjax.success(list);
}
}
🍅 3、使用ajax构造请求
在前端页面的<head></head>添加jquery
<script src="js/jquery.min.js"></script>
添加一个id,包含了文章列表的div
<div id="artListDiv" class="container-right">
<script>
// 初始化方法
function init(){
jQuery.ajax({
url:"/art/mylist",
type:"GET",
data:{},
success:function(res){
if(res.code==200){
// 请求成功
var createHtml = "";
var artList = res.data;
if(artList==null || artList.length==0){
// 未发表文章
createHtml += "<h3 style='margin-left:20px;margin-top:20px'>暂无文章,请先"+
"<a href='blog_add.html'>添加</a>!</h3>";
}else{
for(var i=0;i<artList.length;i++){
var art = artList[i];
createHtml += '<div class="blog">';
createHtml += '<div class="title">'+art.title+'</div>';
createHtml += '<div class="date">'+art.createtime+'</div>';
createHtml += '<div class="desc">';
createHtml += art.content;
createHtml += '</div>';
createHtml += ' <a href="blog_content.html?aid='+
art.id + '" class="detail">查看全文 >></a> ';
createHtml += '<a href="blog_edit.html?aid='+
art.id + '" class="detail">修改 >></a> ';
createHtml += ' <a href="javascript:del('+art.id+')" class="detail">删除 >></a>';
createHtml += '</div>';
}
}
jQuery("#artListDiv").html(createHtml);
}else{
alert("抱歉:操作失败!"+res.msg);
}
}
});
}
init();
</script>
</body>
三、编写注销功能
🍅 1、使用ajax获取请求
由于注销功能,在很多页面都需要用到,所以这里就给一个公共的js
function logout(){
if (confirm("是否确定注销?")){
// 1、在后端删除session信息
jQuery.ajax({
url:"/user/logout",
type:"POST",
data:{},
success:function (res){}
});
// 2、跳转到登录页
location.href = "login.html"
}
}
进入到myblog_list.html中:
<script src="js/logout.js"></script>
<a href="javascript:logout()">注销</a>
🍅 2、处理请求
//注销
@RequestMapping("/logout")
public ResultAjax logout(HttpServletRequest request){
HttpSession session = request.getSession(false);
if (session!=null &&
session.getAttribute(AppVariable.SESSION_USERINFO_KEY)!=null){
session.removeAttribute(AppVariable.SESSION_USERINFO_KEY);
}
return ResultAjax.success(1);
}
四、编写删除文章功能
🍅 1、使用ajax构造请求
在myblog_list.html中添加对应的js代码
主要是根据id删除文章:
1、校验参数
2、将数据返回给后端进行删除操作
2、将结果展示给用户
// 删除文章操作(根据id删除)
function del(aid){
// 参数校验
if(aid=="" || aid<=0){
alert("参数错误");
return false;
}
// 将数据返回给后端进行删除操作
jQuery.ajax({
url:"/art/del",
type:"POST",
data:{
"aid":aid
},
success:function (res){
// 将结果展示给用户
if (res.code==200 && res.data==1){
alert("恭喜:删除成功!");
// 刷新页面
location.href = location.href;
}else {
// 删除失败
alert("抱歉:操作失败!"+res.msg);
}
}
});
}
🍅 2、处理请求
🎄 mapper层:接口方法
在ArticleMapper中添加接口方法
@Delete("delete from articleinfo where id=#{aid} and uid=#{uid}")
int del(@Param("aid")Integer aid,int uid);
🎄 service层
public int del(Integer aid,int uid){
return articleMapper.del(aid,uid);
}
🎄 controller层:处理请求
主要有以下几步:
1、参数校验
2、得到当前登录的用户
3、判断文章的归属人并且进行删除操作
4、将结果返回给前端
// 删除文章
@RequestMapping("/del")
public ResultAjax del(Integer aid,HttpServletRequest request){
if (aid==null || aid<=0){
return ResultAjax.fail(-1,"参数错误!");
}
Userinfo userinfo = SessionUtils.getUser(request);
if (userinfo == null){
return ResultAjax.fail(-1,"请先登录!");
}
int result = articleService.del(aid,userinfo.getId());
return ResultAjax.success(result);
}
五、编写文章添加功能
🍅 1、使用ajax构造请求
(1)非空校验
(2)将用户提交的数据传递给后端
(3)将后端返回的结果展示给用户
进入到blog_add.html中,添加如下代码
function mysub(){
var title = jQuery("#title");
if (title.val.trim()==""){
alert("请先输入标题!");
title.focus();
return false;
}
if (editor.getValue()==""){
alert("请先输入正文!");
return false;
}
jQuery.ajax({
url:"/art/add",
type:"POST",
data:{
"title":title.val(),
"content":editor.getValue()
},
success:function (res){
// 文章添加成功
if (res.code==200 && res.data==1){
if (confirm("恭喜:添加成功!是否继续添加文章?")){
location.href=location.href;
}else{
location.href="myblog_list.html"
}
}else {
//文章添加失败
alert("抱歉:操作失败!"+res.msg);
}
}
});
}
🍅 2、处理请求
🎄mapper层:添加接口方法
@Insert("insert into articleinfo(title,content,uid) values(#{title},#{content},#{uid})")
int add(Articleinfo articleinfo);
🎄service层:返回受影响行数
public int add(Articleinfo articleinfo){
return articleMapper.add(articleinfo);
}
🎄 controller:处理添加文章的请求
主要分为以下几步:
(1)校验参数
(2)组装数据
(3)将数据入库
(4)将结果返回给前端
@RequestMapping("/add")
public ResultAjax add(Articleinfo articleinfo,HttpServletRequest request){
if (articleinfo==null || !StringUtils.hasLength(articleinfo.getTitle())
|| !StringUtils.hasLength(articleinfo.getContent())){
return ResultAjax.fail(-1,"非法参数!");
}
Userinfo userinfo = SessionUtils.getUser(request);
if (userinfo == null){
return ResultAjax.fail(-2,"请先登录");
}
articleinfo.setUid(userinfo.getId());
int result = articleService.add(articleinfo);
return ResultAjax.success(result);
}
六、修改功能:查询到修改的文章
🍅 1、通过ajax构造请求
(1)获取url中的参数:
(例如)localhost:8080/blog_edit.html?aid=2&uid=1
a、通过location.search获取“?”后面的参数
b、去除“?”
c、根据“&”将参数分割成多个数组
d、循环对比key,并返回查询value
创建一个公共的js:
// 根据key获取到对应的url中对应的value function getParamValue(key){ var params = location.search; if (params.indexOf("?")>=0){ params=params.substring(1); var paramArray=params.split("&"); if (paramArray.length>=1){ for (var i = 0; i < paramArray.length; i++) { var item=paramArray[i].split("="); if (item[0]==key[1]){ return item; } } } } return null; }
(2)校验 aid
(3)查询文章详情
(4)将文章的详情信息展示到页面
首先在head中添加得到参数的方法
<script src="js/urlutils.js"></script>
在blog_edit.html中的<script></script>中添加代码
var aid=getParamValue("aid");
function init(){
if (aid==null || aid<=0){
alert("非法参数")
return false;
}
jQuert.ajax({
url:"/art/getdetail",
type:"GET",
data:{
"aid":aid
},
success:function(res){
if (res.code==200 && res.data!=null && res.data.id>0){
jQuery("#title").val(res.data.title);
initEdit(res.data.content);
}else {
alert("抱歉,查询失败!"+res.msg);
}
}
});
}
init();
🍅 2、处理请求
🎄Mapper层:添加接口
进入到ArticleMapper中
@Select("select * from aeticleinfo where id=#{aid} and uid=#{uid}")
Articleinfo getArticleByIdAndUid(@Param("aid")int aid,
@Param("uid")int uid);
🎄service层
public Articleinfo getArticleByIdAndUid(int aid,int uid){
return ArticleMapper.getArticleByIdAndUid(aid,uid);
}
🎄controller层
(1)校验参数
(2)得到当前登录用户id
(3)查询文章并且校验权限
@RequestMapping("/update_init")
public ResultAjax updateInit(Integer aid,HttpServletRequest request){
if (aid==null || aid<=0 ){
return ResultAjax.fail(-1,"参数有误!");
}
Userinfo userinfo = SessionUtils.getUser(request);
if (userinfo == null){
return ResultAjax.fail(-2,"请先登录!");
}
Articleinfo articleinfo = articleService.getArticleByIdAndUid(aid,userinfo.getId())
return ResultAjax.success(articleinfo);
}
七、修改功能:修改查询到的文章
🍅 1、通过ajax构造请求
进入到blog_edit.html中
<button onclick="doUpdate()">修改文章</button>
function doUpdate(){
if(title.val().trim()==""){
alert("请先输入标题!");
title.focus();
return false;
}
if(editor.getValue()==""){
alert("请先输入正文!");
return false;
}
jQuery.ajax({
url:"/art/update",
type:"POST",
data:{
"id":aid,
"title":title.val(),
"content":editor.getValue()
},
success:function(res){
if(res.code==200 && res.data==1){
// 修改成功
alert("恭喜:修改成功!");
// 跳转到我的文章管理员
location.href = "myblog_list.html";
}else if(res.code==-2){
alert("请先的登录!");
location.href = "login.html";
}
else{
alert("抱歉:修改失败!"+res.msg);
}
}
});
}
🍅 2、处理请求
🎄 mapper层
进入到ArticleMappe中
@Update("update articleinfo set title=#{title},content=#{content} where id=#{id} and uid=#{uid}")
int update(Articleinfo articleinfo);
🎄 service层
进入到ArticleService中
public int update(Articleinfo articleinfo){
return articleMapper.update(articleinfo);
}
🎄 controller层
(1)参数校验
(2)获取登录用户
(3)修改文章,并且校验归属人
(4)返回结果
// 修改文章信息
@RequestMapping("/update")
public ResultAjax update(Articleinfo articleinfo,HttpServletRequest request){
if (articleinfo==null || !StringUtils.hasLength(articleinfo.getTitle())
|| !StringUtils.hasLength(articleinfo.getContent())
|| articleinfo.getId() == 0){
return ResultAjax.fail(-1,"非法参数!");
}
Userinfo userinfo = SessionUtils.getUser(request);
if (userinfo == null){
return ResultAjax.fail(-2,"请先登录!");
}
articleinfo.setUid(userinfo.getId());
int result = articleService.update(articleinfo);
return ResultAjax.success(result);
}
八、文章详情
🍅 1、前端页面编写
进入到blog_content.html中
🎄 给定id值
//左侧的个人信息
<img id="photo" src="img/avatar.png" class="avtar" alt="">
<h3 id="username"></h3>
<a href="http:www.github.com">github 地址</a>
//右侧的博客信息
<!-- 博客标题 -->
<h3 id="title"></h3>
<!-- 博客时间 -->
<div class="date">
发布时间:<span id="createtime"></span>
/
阅读量:<span id="rcount"></span>
</div>
🎄 使用ajax构造请求
var aid = getParamValue("aid");
//初始化页面
function init(){
var aid = getParamValue("aid");
if (aid==null || aid<=0){
alert("参数有误!");
return false;
}
jQuery.ajax({
url:"/art/detail",
type:"GET",
data:{
"aid":aid
},
success: function (res){
if (res.code==200 && res.data!=null){
var user = res.data.user;
var art = res.data.art;
if (user!=null){
if(user.photo!=""){
jQuery("#photo").art("src,user.photo");
}
jQuery("#username").html(user.username);
jQuery("#artcount").html(user.artCount);
}else{
alert("抱歉:查询失败!"+reg.msg);
}
if(art!=null){
jQuery("#title").html(art.title);
jQuery("#createtime").html(art.createtime);
jQuery("#rcount").html(art.rcount);
initEdit(art.content);
}else{
alert("抱歉:查询失败!"+reg.msg)
}
}else{
alert("抱歉,查询失败!"+res.msg);
}
}
});
}
🍅 2、处理请求
首先在userinfoVO中添加一个变量:
private int artCount; //用户发布的文章总数
🎄 mapper层
首先在Articleinfo中添加一条sql:根据id查询文章
@Select("select * from articleinfo where id=#{aid}")
Articleinfo getDetaiById(@Param("aid") int aid);
@Select("select count(*) from articleinfo where uid=#{uid}")
int getArticleByUid(@Param("uid")int uid);
然后再Userinfo中添加一条sql:根据id查询用户
@Select("select * from us erinfo where id=#{uid}")
UserinfoVO getUserById(@Param("uid") int uid);
🎄 service层
ArticleService中
public Articleinfo getDetail(int aid){
return articleMapper.getDetaiById(aid);
}
public int getArtCountByUid(int uid){
return articleMapper.getArticleByUid(uid);
}
UserService中
public UserinfoVO getUserById(int uid){
return userMapper.getUserById(uid);
}
🎄 controller层
(1)参数校验
(2)查询文章详情
(3)根据uid查询用户的详情
(4)根据uid查询用户发表的总文章数
(5)组装数据
(6)返回结果给前端
首先创建一个线程池
使用ThreadPoolExecutor来构造线程池,该方法其实和JDK中的ThreadPool区别就在于,ThreadPoolExecutor式通过参数的方式去设置,而ThreadPool是通过构造方法,ThreadPoolExecutor中的方式更加直观简单。
ThreadPool然后将其注入依赖
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
处理请求:
先注入依赖:
@Autowired
private UserService userService;
使用多线程并发编程就可以很快的查询到对应的文章详情了。
//查询文章详情页
@RequestMapping("/detail")
public ResultAjax detail(Integer aid) throws ExecutionException, InterruptedException {
if(aid==null || aid<=0){
return ResultAjax.fail(-1,"非法参数");
}
Articleinfo articleinfo = articleService.getDetail(aid);
if (articleinfo == null || articleinfo.getId() <= 0){
return ResultAjax.fail(-1,"非法参数!");
}
//根据uid查询用户的详情
FutureTask<UserinfoVO> userTask = new FutureTask(()->{
return userService.getUserById(articleinfo.getUid());
});
taskExecutor.submit(userTask);
//根据uid查询用户发表的文章数
FutureTask<Integer> artCountTask = new FutureTask<>(()->{
return articleService.getArtCountByUid(articleinfo.getUid());
});
taskExecutor.submit(artCountTask);
//等待任务(线程池)执行完
UserinfoVO userinfoVO = userTask.get();
int artCount = artCountTask.get();
userinfoVO.setArtCount(artCount);
//数据组装
HashMap<String,Object> result = new HashMap<>();
result.put("user",userinfoVO);
result.put("art",articleinfo);
return ResultAjax.success(result);
}
九、阅读量设置
🍅 使用ajax构造请求
//访问量+1
function increamentRCount(){
if (aid==null || aid<=0 ){
return false;
}
jQuery.ajax({
url:"/art/increment_rcount",
type:"POST",
data:{
"aid":aid
},
success:function(res){}
});
}
increamentRCount();
🍅 处理请求
🎄 mapper层
@Update("update articleinfo set rcount=rcount+1 where id=#{aid}")
int incrementRCount(@Param("aid") int aid);
🎄 service层
public int incrementRCount(int aid){
return articleMapper.incrementRCount(aid);
}
🎄 controller层
(1)校验参数
(2)更改数据库
(3)返回结果
@RequestMapping("/increment_rcount")
public ResultAjax incrementRCount(Integer aid){
if (aid==null || aid<=0){
return ResultAjax.fail(-1,"参数有误!");
}
int result = articleService.incrementRCount(aid);
return ResultAjax.success(result);
}
十、分页处理
核心参数:
(1)页码(当前在第几页)
(2)每页显示的最大条数(前端灵活的控制分页功能)
后端分页返回:
(1)当前页面的文章列表
(2)总共有多少页:根据博客总条数/每一页显示条数的结果,向上取整(比如3.3333 取为4)
🍅 1、前端处理
首先将对应的js引入进,来根据总条数每页显示条数(向上取整
添加一个id属性:主要是为了构造出每一条博客的div出来
<!-- 每一篇博客包含标题, 摘要, 时间 -->
<div id="artListDiv">
</div>
然后初始化数据:
(1)得到url中的分页参数
(2)qing求后端接口
(3)将结果返回给用户
var psize = 2; // 每页显示条数
var pindex = 1; // 页码
var totalpage = 1; // 总共有多少页
// 初始化数据
function init(){
// 1.处理分页参数
psize = getParamValue("psize");
if(psize==null){
psize = 2; // 每页显示条数
}
pindex = getParamValue("pindex");
if(pindex==null){
pindex = 1; // 页码
}
jQuery("#pindex").html(pindex);
// 2.请求后端接口
jQuery.ajax({
url:"/art/getlistbypage",
type:"GET",
data:{
"pindex":pindex,
"psize":psize
},
success:function(res){
// 3.将结果展示给用户
if(res.code==200 && res.data!=null){
var createHtml = "";
if(res.data.list!=null && res.data.list.length>0){
// 有文章
totalpage = res.data.size;
jQuery("#pszie").html(totalpage);
var artlist = res.data.list;
for(var i=0;i<artlist.length;i++){
var art = artlist[i]; // 文章对象
createHtml += '<div class="blog" >';
createHtml += '<div class="title">'+art.title+'</div>';
createHtml += '<div class="date">'+art.createtime+'</div>';
createHtml += '<div class="desc">'+art.content+'</div>';
createHtml += '<a href="blog_content.html?aid='+
art.id+'" class="detail">查看全文 >></a>';
createHtml += '</div>';
}
}else{
// 暂无文章
createHtml += '<h3 style="margin-top:20px;margin-left:20px;">暂无文章!</h3>';
}
jQuery("#artListDiv").html(createHtml);
}else{
alert("抱歉:查询失败!"+res.msg);
}
}
});
}
init();
//点击首页
function doFirst(){
if (pindex<=1){
alert("已经在首页了,无需跳转");
return false;
}
location.href = "blog_list.html";
}
//跳转到末页
function doLast(){
if (pindex>=totalpage){
alert("已经在末页了,无需跳转!");
return false;
}
location.href="blog_list.html?pindex="+totalpage;
}
//点击上一页
function doBefore(){
if (pindex<=1){
alert("已经在首页了,无需跳转");
return false;
}
location.href="blog_list.html?pindex="+(parseInt(pindex)-1);
}
//点击下一页
function doNext(){
if (pindex>=totalpage){
alert("已经在末页了,无需跳转!");
return false;
}
location.href="blog_list.html?pindex="+(parseInt(pindex)+1);
}
🍅 2、后端处理
推导到分页公式:每页显示2条数据
每次只有偏移量offset变了
# 第一页 mysql> select * from articleinfo order by id limit 2 offset 0; # 第二页 mysql> select * from articleinfo order by id limit 2 offset 2; # 第三页 mysql> select * from articleinfo order by id limit 2 offset 4; .....
假设每条显示条数为pageSize,页码为pageIndex
故而推到出的公式为:offset = pageSize*(pageIndex-1)
🎄 mapper层
@Select("select * from articleinfo order by id desc limit #{psize} offset #{offset} ")
public List<Articleinfo> getListByPage(@Param("psize") int psize,@Param("offset")int offset);
@Select("select count(*) from articleinfo")
int getCount();
🎄 service层
public List<Articleinfo> getListByPage(int psize,int offset){
return articleMapper.getListByPage(psize, offset);
}
🎄 controller层
(1)加工矫正
(2)并发进行文章列表和总页数的查询
(3)组装数据
(4)将结果返回给前端
@RequestMapping("/getlistbypage")
public ResultAjax getListByPage(Integer pindex,Integer psize) throws ExecutionException, InterruptedException {
if (pindex == null || pindex < 1){
pindex = 1;
}
if (psize==null || psize<1){
psize = 2;
}
//查询分页列表数据:
Integer finalPsize = psize;
Integer finalPindex = pindex;
FutureTask<List<Articleinfo>> listTask = new FutureTask<>(()->{
int finalOffset = finalPsize *(finalPindex -1);
return articleService.getListByPage(finalPsize,finalOffset);
}) ;
//查询总页数
FutureTask<Integer> sizeTask = new FutureTask<>(()->{
int totalCount = articleService.getCount();
double sizeTemp = (totalCount * 1.0) / (finalPsize * 1.0);
return (int) Math.ceil(sizeTemp);
});
taskExecutor.submit(listTask);
taskExecutor.submit(sizeTask);
List<Articleinfo> list = listTask.get();
int size = sizeTask.get();
HashMap<String,Object> map = new HashMap<>();
map.put("list",list);
map.put("size",size);
return ResultAjax.success(map);
}
十一、密码加盐
先修改数据库中的字段:
mysql> alter table userinfo modify password varchar(65) not null;
🍅 1、定义加密规则
加密流程:
使用加盐算法,也就是使用一个不重复随机的盐值+密码,得到一个无规律的密码 (1)生成一个盐值
(2)(根据盐值 + 固定密码)进行加密 -> md5(盐值 + 密码)= 最终密码
(3)将[盐值]+ [分隔符$] + 最终密码保存到数据库中
验证密码流程:
(1)得到盐值
(2)md5(盐值+待验证密码)-> 最终待验证的密码
(3)对比最终待验证的密码和数据中的最终密码是否相同-> 相同密码正确
创建一个加密解密的类:
public class PasswordUtils {
//加盐加密
public static String encrypt(String password){
String salt = UUID.randomUUID().toString().replace("-","");
String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
return salt+"$"+finalPassword;
}
//待验证密码
public static boolean decrypt(String password,String dbPassword){
if (!StringUtils.hasLength(password) || !StringUtils.hasLength(dbPassword) ||
dbPassword.length() != 65){
return false;
}
String[] dbPasswordArray = dbPassword.split("$");
if (dbPasswordArray.length!=2){
return false;
}
String salt = dbPasswordArray[0];
String dbFinalPassword = dbPasswordArray[1];
String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
if (finalPassword.equals(dbFinalPassword)){
return true;
}
return false;
}
}
🍅 2、修改UserController代码
reg()方法中添加以下代码:
userinfo.setPassword(PasswordUtils.encrypt(userinfo.getPassword()));
在login()方法中添加以下代码
//使用对象中的密码和用户输入的密码进行比较
if (!PasswordUtils.decrypt(userinfoVO.getPassword(),userinfo.getPassword())){
return ResultAjax.fail(-2,"用户名或者密码错误!");
}
十二、session升级存储到redis
这个部分,还需要部署redis,先使用finalShell或者xshell远程连接Linux服务器,然后参考后面的博客去安装Redis 安装以及配置隧道连接_点子李的博客-CSDN博客
🍅 1、先添加redis相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.13</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
🍅 2、添加redis配置信息(properties)
spring.redis.host=39.107.159.218
spring.redis.port=6379
spring.redis.password=
spring.redis.database=2
spring.session.store-type=redis
打开登录页面,发现存储成功
十三、实现自定义拦截器
Spring MVC
中的拦截器(Interceptor
)类似于ServLet中的过滤器(Filter
),它主要用于拦截用户请求并作出相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
首先创建拦截器
public class LoginIntercept implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session!=null && session.getAttribute(AppVariable.SESSION_USERINFO_KEY)!=null){
//用户登录
return true;
}
response.sendRedirect("/login.html");
return false;
}
}
然后创建一个类,定义拦截器规则文章来源:https://www.toymoban.com/news/detail-661160.html
文章来源地址https://www.toymoban.com/news/detail-661160.html
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginIntercept())
.addPathPatterns("/**")
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/reg")
.excludePathPatterns("/user/detail")
.excludePathPatterns("/art/getlistbypage")
.excludePathPatterns("/editor.md/*")
.excludePathPatterns("/img/*")
.excludePathPatterns("/js/*")
.excludePathPatterns("/css/*")
.excludePathPatterns("/login.html")
.excludePathPatterns("/reg.html")
.excludePathPatterns("/blog_list.html")
.excludePathPatterns("/blog_content");
}
}
到了这里,关于项目实战 — 博客系统③ {功能实现}的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!