实战11:SpringBoot和Vue实现登录、注册和异常处理

目录

一、异常统一处理类

1、Result包装类

2、系统异常统一处理

二、SpringBoot实现登录、注册、个人信息

三、Vue实现登录、注册、个人信息

1、登录: 登录方法 、表单数据校验、登录表单、跳转注册

2、注册:页面布局、表单数据校验 、注册 、返回登录

3、个人信息:卡片和表单 、localStorage存储登录用户全局用户数据 、根据用户名称获取用户数据 、修改用户数据

4、路由配置

5、用户全局状态


一、异常统一处理类

1、Result包装类

package com.example.demo.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


/*
接口统一返回包装类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private String code;
    private String msg;
    private Object data;

    public static Result success() {
        return new Result(Constants.CODE_200, "", null);
    }

    public static Result success(Object data) {
        return new Result(Constants.CODE_200, "", data);
    }

    public static Result error(String code, String msg) {
        return new Result(code, msg, null);
    }

    public static Result error() {
        return new Result(Constants.CODE_500, "系统错误", null);
    }

}

2、系统异常统一处理

1)业务异常类

package com.example.demo.exception;

import lombok.Getter;

/**
 * 自定义异常
 */
@Getter
public class ServiceException extends RuntimeException {
    private String code;

    public ServiceException(String code, String msg) {
        super(msg);
        this.code = code;
    }
}

2)统一异常处理

package com.example.demo.exception;


import com.example.demo.common.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.xml.ws.Response;

@RestControllerAdvice
public class GlobleExceptionHandler {
    @ResponseBody
    @ExceptionHandler(ServiceException.class)
    public Result handle(ServiceException se){
        return Result.error(se.getCode(),se.getMessage());

    }

}

接口常量

package com.example.demo.common;

public interface Constants {
    String CODE_200 = "200";
    String CODE_401 = "401";//权限不足    
    String CODE_400 = "400";//参数错误    
    String CODE_500 = "500";
    String CODE_600 = "600";
}

二、SpringBoot实现登录、注册、个人信息


@PostMapping("/login")
public Result login(@RequestBody UserDto userDto){
    String username = userDto.getUsername();
    String password = userDto.getPassword();
    if (StrUtil.isBlank(username)||StrUtil.isBlank(password)){
        return Result.error(Constants.CODE_400,"参数错误");
    }
    return Result.success(userService.login(userDto));
}

public Result register(@RequestBody UserDto userDto) {
    String username = userDto.getUsername();
    String password = userDto.getPassword();

    if (StrUtil.isBlank(username)||StrUtil.isBlank(password)){
        return Result.error(Constants.CODE_400,"参数错误");
    }
    return Result.success(userService.register(userDto));
}

@GetMapping("/username/{username}")
public Result findOne(@PathVariable String username){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("username", username);
    return Result.success(userService.getOne(queryWrapper));
}
package com.example.demo.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.common.Constants;
import com.example.demo.dto.UserDto;
import com.example.demo.entity.User;
import com.example.demo.exception.ServiceException;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author cizhu
 * @since 2023-11-12
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public UserDto login(UserDto userDto) {
        User one = getUserInfo(userDto);
        if (one != null) {
            BeanUtil.copyProperties(one, userDto, true); //
            return userDto;
        } else {
            throw new ServiceException(Constants.CODE_600, "用户名或密码错误");             //自定义异常
        }
    }

//        List<User> list = list(queryWrapper);
//        return list.size()!=0 ;
//    }


    @Override
    public User register(UserDto userDto) {
        User one = getUserInfo(userDto);
        if (one == null) {
            one = new User();
            BeanUtil.copyProperties(userDto, one, true);
            save(one);
        } else {
            throw new ServiceException(Constants.CODE_600, "用户已存在");
        }

        return one;
    }

    private User getUserInfo(UserDto userDto) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", userDto.getUsername());
        queryWrapper.eq("password", userDto.getPassword());
        User one;
        try {
            one = getOne(queryWrapper);
        } catch (Exception e) {
            throw new ServiceException(Constants.CODE_500, "系统异常");             //自定义异常
        }
        return one;
    }
}
package com.example.demo.service;

import com.example.demo.dto.UserDto;
import com.example.demo.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author cizhu
 * @since 2023-11-12
 */
public interface IUserService extends IService<User> {

    UserDto login(UserDto userDto);

    User register(UserDto userDto);
}

三、Vue实现登录、注册、个人信息

1、登录: 登录方法 、表单数据校验、登录表单、跳转注册

<template>
  <div class="wrapper">
    <div style="margin: 200px auto;background-color: #fff; width: 350px; padding: 20px; border-radius: 10px;">
      <div style="margin: 20px 0;text-align: center; font-size: 24px;"><b>登录</b></div>
      <el-form :rules="rules" :model="user" ref="userform">
        <el-form-item prop="username">
          <el-input size="medium" style="margin:10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input size="medium" style="margin:10px 0" prefix-icon="el-icon-lock" show-password
            v-model="user.password"></el-input>
        </el-form-item>
        <el-form-item style="margin: 10px 0; text-align: right">
          <el-button type="primary" size="small" autocomplete="off" @click="login">登录</el-button>
          <el-button type="warning" size="small" autocomplete="off" @click="$router.push('/register')">注册</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Login',
  data() {
    return {
      user: {},
      rules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 10个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 3, max: 20, message: '长度在 3 到 20个字符', trigger: 'blur' }
        ]
      }
    }
  },

  methods: {
    login() {
      this.$refs['userform'].validate((valid) => {
        if (valid) {
          this.request.post("user/login", this.user).then(res => {
            if (res.code === '200') {
              localStorage.setItem("user",JSON.stringify(res.data))
              this.$router.push("/")
              this.$message.success("登录成功")
            } else {
              this.$message.error(res.msg);
            }
          })
        }
      });
    },
  }
}
</script>
<style>
.wrapper {
  height: 100vh;
  background-image: linear-gradient(to bottom right, #FC4668, #3F5EFB);
  overflow: hidden;
}
</style>

2、注册:页面布局、表单数据校验 、注册 、返回登录

<template>
  <div class="wrapper">
    <div style="margin: 100px auto;background-color: #fff; width: 350px; padding: 20px; border-radius: 10px;height: 400px;">
      <div style="margin: 20px 0;text-align: center; font-size: 24px;"><b>注册</b></div>
      <el-form :rules="rules" :model="user" ref="userform">
        <el-form-item prop="username">
          <el-input placeholder="输入账号" size="medium" style="margin:5px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input placeholder="输入密码" size="medium" style="margin:5px 0" prefix-icon="el-icon-lock" show-password
            v-model="user.password"></el-input>
        </el-form-item>
        <el-form-item prop="confirmPassword">
          <el-input placeholder="输入确认密码" size="medium" style="margin:5px 0" prefix-icon="el-icon-lock" show-password
            v-model="user.confirmPassword"></el-input>
        </el-form-item>
        <el-form-item style="margin: 10px 0; text-align: right">
          <el-button type="primary" size="small" autocomplete="off" @click="register">注册</el-button>
          <el-button type="warning" size="small" autocomplete="off" @click="$router.push('/login')">返回登录</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Login',
  data() {
    return {
      user: {},
      rules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 10个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 3, max: 20, message: '长度在 3 到 20个字符', trigger: 'blur' }
        ],
        confirmPassword: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 3, max: 20, message: '长度在 3 到 20个字符', trigger: 'blur' }
        ]
      }
    }
  },

  methods: {
    register() {
      this.$refs['userform'].validate((valid) => {
        if (valid) {
          if (this.user.password!=this.user.confirmPassword) {
            this.$message.error("两次输入密码不一致")
            return false
          } else {
            
          }
          this.request.post("user/register", this.user).then(res => {
            if (res.code === '200') {
              this.$message.success("注册成功")
            } else {
              this.$message.error(res.msg);
            }
          })
        }
      });
    },
  }
}
</script>
<style>
.wrapper {
  height: 100vh;
  background-image: linear-gradient(to bottom right, #FC4668, #3F5EFB);
  overflow: hidden;
}
</style>

3、个人信息:卡片和表单 、localStorage存储登录用户全局用户数据 、根据用户名称获取用户数据 、修改用户数据

<template>
  <el-card style="width: 500px">
    <el-form label-width = "80px" size = "small">
        <el-form-item label="用户名">
          <el-input v-model="form.username" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="昵称">
          <el-input v-model="form.nickname" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="邮箱">
          <el-input v-model="form.email" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="form.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="form.address" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="save">确 定</el-button>
        </el-form-item>
      </el-form>
  </el-card>

</template>
<script>

export default{
  name: "Person",
  data(){
    return{
      form:{},
      user:localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")):{}


    }
  },
  created(){
    this.request.get("/user/username/"+this.user.username).then(res=>{
      if (res.code == '200') {
        this.form =res.data
        
      }

    })

  },
  methods:{
    save() {
        this.request.post("/user", this.form).then(res => {
            if (res.data) {
                this.$message.success("保存成功");
            }else {
                this.$message.error("保存失败");
            }
        });
    },
  }
}
</script>
<style>
</style>

4、路由配置

{
    path: '/',
    name: 'Manage',
    redirect: '/home',
    component: () => import('../views/Manage.vue'),
    children: [
      {path: 'home',name: '首页',component: ()=>import ('../views/Home.vue') },
      {path: 'user',name: '用户管理',component: ()=>import ('../views/User.vue') },
      {path: 'person',name: '个人信息',component: ()=>import ('../views/Person.vue') }
    ]
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import( '../views/Login.vue')
  },
  {
    path: '/register',
    name: 'Register',
    component: () => import( '../views/Register.vue')
  }

5、用户全局状态

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    currentPathName:''
  },
  mutations:{
    setPath(state){
      state.currentPathName= localStorage.getItem("currentPathName")
    }
  }
})
export default store