详解SpringBoot项目整合Vue做一个完整的用户注册功能

目录
  • 前言
  • 1 实现用户注册流程
    • 1.1 用户注册完整流程
    • 1.2 用户注册信息及校验
  • 2 后台接口设计
    • 2.1 上传头像接口
    • 2.2 用户注册接口
  • 3 后端代码实现
    • 3.1 用户头像上传接口编码实现
    • 3.2 用户注册接口
  • 4 前端代码实现
    • 4.1 完成用户注册界面vue组件编码
    • 4.2 工具类中增加校验方法
    • 4.3 API文件中添加用户注册方法
    • 4.4 全局方法中添加用户注册方法
    • 4.5 路由列表中添加用户注册组件
    • 4.6 登录组件中添加用户注册的跳转链接
    • 4.7 路由跳转控制中添加白名单
  • 5 效果体验
  • 6 写在最后

前言

用户注册功能是每一个系统的入口门面功能,很多人可能会以为很简单,不就是一个简单的CRUD吗?其实不然,要把前后端功能都做出来,页面跳转也没问题,还真不简单。这次笔者做这么一个看似简单的用户注册功能就花了足足两天多时间,中间调试和解决Bug也花了好长时间。这次我就把自己做出的完整功能的实现过程作了一个提炼分享到我的公众号上来。希望有需要了解如何实现用户注册完整过程的读者朋友能够仔细看一看。

说明:本文前后端代码的实现分别在本人之前二次开发的开源项目vue-element-adminvueblog两个项目的基础上进行

1 实现用户注册流程

1.1 用户注册完整流程

1.2 用户注册信息及校验

2 后台接口设计

2.1 上传头像接口

2.1.1 接口url

http://localhost:8081/blog/upload/user/avatar

2.1.2 请求类型

POST

2.1.3 接口入参

参数名称 参数类型 是否必传 备注
file MultipartFile 多媒体图片文件

2.1.4 接口出参

参数名称 参数类型 示例值 备注
status Integer 200 状态码:200-成功; 500-失败
msg String “success” 响应信息:“success”-上传头像成功; "upload file failed"-上传头像失败
data String vueblog2022.oss-cn-shenzhen.aliyuncs.com/avatar/63be… 上传头像成功后的下载地址

2.2 用户注册接口

2.2.1 接口url

http://localhost:8081/blog/user/reg

2.2.2 请求类型

POST

2.2.3 接口入参

参数名称 参数类型 是否必填 备注
username String 用户账号
nickname String 用户昵称
password String 用户登录密码
userface String 用户头像链接地址
phoneNum Long 用户手机号码
email String 用户邮箱地址

2.2.3 接口出参

参数名称 参数类型 示例值 备注
status Integer 200 响应码: 200-成功;500-失败
msg String 注册成功 响应消息
data Integer 0 注册成功标识:0-注册成功;1-用户名重复; null-内部服务异常

3 后端代码实现

3.1 用户头像上传接口编码实现

文件上传,这里选用了阿里云的对象存储,需要先开通阿里云对象存储服务,关于如何开通阿里云短信服务并将阿里云对象存储服务集成到SpringBoot项目中,请参考我之前发布的文章SpringBoot项目集成阿里云对象存储服务实现文件上传

3.1.1 服务层编码

新建OssClientService类继承阿里云对象存储服务SDK完成图片上传功能

@Service
public class OssClientService {

    @Resource
    private OssProperties ossProperties;

    private static final Logger logger =  LoggerFactory.getLogger(OssClientService.class);

    public String uploadFile(MultipartFile file){
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(ossProperties.getEndPoint(), ossProperties.getAccessKey(),
                ossProperties.getSecretKey());
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String objectName = "avatar/" + uuid + ".png";
        String imageUrl = null;
        try {
            InputStream inputStream =  file.getInputStream();
            ossClient.putObject(ossProperties.getBucketName(), objectName, inputStream);
            imageUrl = "https://" + ossProperties.getBucketName() + "." + ossProperties.getEndPoint() + "/" + objectName;
        } catch (OSSException oe) {
            logger.error("Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.");
            logger.error("Error Message:" + oe.getErrorMessage());
            logger.error("Error Code:" + oe.getErrorCode());
            logger.error("RequestId: " + oe.getRequestId());
            logger.error("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            logger.error("Caught an ClientException, which means the client encountered a serious internal problem " +
                    "while trying to communicate with OSS,such as not being able to access the network");
            logger.error("Error Message:" + ce.getErrorMessage());
        } catch (FileNotFoundException fe) {
            logger.error("file not found exception");
            logger.error("Error Message:" + fe.getMessage(), fe);
        } catch (IOException exception){
            logger.error("file get input stream error, caused by " + exception.getMessage(), exception);
        }
        finally {
            if (ossClient!=null) {
                ossClient.shutdown();
            }
        }
        return imageUrl;
    }
}

注意:升级到3.9.1版本后的aliyun-sdk-oss需要在每次上传文件时新建一个OSS实例, 上传完文件之后再调用shutdown方法关闭这个实例

3.1.2 控制器层编码

新建UploadFileController类完成从前端接收附件参数,并调用OssClientService服务实现图片上传

@RestController
@RequestMapping("/upload")
public class UploadFileController {

    @Resource
    private OssClientService ossClientService;

    @PostMapping("/user/avatar")
    @ApiOperation(value = "userAvatar", notes = "用户上传头像接口",
    produces = "application/octet-stream", consumes = "application/json")
    public RespBean uploadUserAvatar(HttpServletRequest request){
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        // 获取上传文件对象
        MultipartFile file = multipartRequest.getFile("file");
        RespBean respBean = new RespBean();
        String downloadUrl = ossClientService.uploadFile(file);
        if (!StringUtils.isEmpty(downloadUrl)) {
            respBean.setStatus(200);
            respBean.setMsg("success");
            respBean.setData(downloadUrl);
        } else {
            respBean.setStatus(500);
            respBean.setMsg("upload file failed");
        }
        return respBean;
    }
}

3.2 用户注册接口

3.2.1 数据库访问层编码

UserMapper接口类中新增注册用户抽象方法

int registerUser(UserDTO user);

然后在UserMapper.xml文件中完成用户数据入库sql编写

<insert id="registerUser" useGeneratedKeys="true" keyProperty="id" parameterType="org.sang.pojo.dto.UserDTO">
        INSERT INTO user(username, nickname, password, phoneNum,email, userface, regTime,enabled)
        values(#{username,jdbcType=VARCHAR},#{nickname,jdbcType=VARCHAR},
        #{password,jdbcType=VARCHAR}, #{phoneNum,jdbcType=BIGINT}, #{email,jdbcType=VARCHAR},
        #{userface,jdbcType=VARCHAR},now(),1)
    </insert>

3.2.2 服务层编码

CustomUserDetailsService接口类中添加注册用户抽象方法

int registerUser(UserDTO user);

然后在 CustomUserDetailsService接口类的实现类UserService类中完成用户注册逻辑

    @Override
    public int registerUser(UserDTO user) {
        // 判断用户是否重复注册
        UserDTO userDTO  = userMapper.loadUserByUsername(user.getUsername());
        if (userDTO != null) {
            return 1;
        }
        //插入用户, 插入之前先对密码进行加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setEnabled(1);//用户可用
        int result = userMapper.registerUser(user);
        //配置用户的角色,默认都是普通用户
        List<Integer> roleIds = Arrays.asList(2);
        int i = rolesMapper.setUserRoles(roleIds, user.getId());
        boolean b = i == roleIds.size() && result == 1;
        if (b) {
            // 注册成功
            return 0;
        } else {
            // 注册失败
            return 2;
        }
    }

3.2.3 控制器层编码

LoginRegController类中完成用户登录接口从前端接收参数到调用UserService服务类完成用户注册业务

@RequestMapping(value = "/login_page", method = RequestMethod.GET)
    @ApiOperation(value = "loginPage", notes = "尚未登录跳转", produces = "application/json",
            consumes = "application/json", response = RespBean.class)
    public RespBean loginPage() {
        return new RespBean(ResponseStateConstant.UN_AUTHORIZED, "尚未登录,请登录!");
    }

    @PostMapping("/user/reg")
    @ApiOperation(value = "reg", notes = "用户注册", produces = "application/json",
            consumes = "application/json", response = RespBean.class)
    public RespBean reg(@RequestBody UserDTO user) {
        int result = userService.registerUser(user);
        if (result == 0) {
            //成功
            return new RespBean(ResponseStateConstant.SERVER_SUCCESS, "注册成功!");
        } else if (result == 1) {
            return new RespBean(ResponseStateConstant.DUPLICATE_ERROR, "用户名重复,注册失败!");
        } else {
            //失败
            return new RespBean(ResponseStateConstant.SERVER_ERROR, "注册失败!");
        }
    }

由于以上两个接口都是需要放开权限控制的,因此完成以上两个接口的编码后还需要在security配置类WebSecurityConfig类中支持匿名访问

只需要在configure(HttpSecurity http)方法中添加如下几行代码即可

http.authorizeRequests()
                .antMatchers("/user/reg").anonymous()
                .antMatchers("/upload/user/avatar").anonymous()

完成后端编码后可以启动Mysql服务和redis服务,然后运行BlogserverApplication类中的Main方法成功后就可以通过postman工具测试接口了

4 前端代码实现

4.1 完成用户注册界面vue组件编码

src/views目录下新建register文件夹,然后在register目录下新建index.vue文件

完成用户注册组件编码

这里的文件上传选择了element-ui组件库中的upload组件

<template>
    <div class="register-container">
        <el-form :model="registerModel" :rules="rules" ref="registerForm" label-width="100px" class="register-form">
            <el-form-item label="用户账号" prop="userAccount" required>
                <el-input
                  v-model="registerModel.userAccount"
                  placeholder="请输入用户名"/>
            </el-form-item>
            <el-form-item label="用户昵称" prop="nickName" required>
                <el-input
                  v-model="registerModel.nickName"
                  type="text"
                  placeholder="请输入用户昵称"/>
            </el-form-item>
            <el-form-item label="登录密码" prop="password" required>
                <el-input
                  v-model="registerModel.password"
                  type="password"
                  placeholder="请输入密码"
                  suffix-icon="el-icon-lock"/>
            </el-form-item>
            <el-form-item label="确认密码" prop="password2" required>
                <el-input
                  v-model="registerModel.password2"
                  type="password"
                  :show-password="false"
                  placeholder="请再次输入密码"
                  suffix-icon="el-icon-lock" />
            </el-form-item>
            <el-form-item label="头像">
                <el-upload class="avatar-uploader"
                    :show-file-list="false"
                    accept="image"
                    :action="uploadAvatarUrl"
                    :on-preview="previewAvatar"
                    :before-upload="beforeAvartarUpload"
                    :on-success="handleSuccessAvatar"
                >
                    <img v-if="avatarUrl" :src="avatarUrl" class="avatar" />
                    <div v-else class="upload-btn" >
                        <el-button>点击上传头像</el-button>
                        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10M</div>
                    </div>
                </el-upload>
            </el-form-item>
            <el-form-item label="手机号" prop="phoneNum" required>
                <el-input type="tel"
                v-model="registerModel.phoneNum"
                placeholder="请输入手机号"
                />
            </el-form-item>
            <el-form-item label="邮箱" prop="email">
                <el-input type="email"
                v-model="registerModel.email"
                placeholder="请输入你的邮箱" />
            </el-form-item>
            <el-form-item class="btn-area">
               <el-button class="submit-btn" type="primary" :loading="onLoading"  @click="handleRegister('registerForm')">提交</el-button>
               <el-button class="reset-btn" type="info" @click="resetForm('registerForm')">重置</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script>
import { Message } from 'element-ui'
import { isNumber, validatePhoneNum, validatePassword, validEmail } from '@/utils/validate'
export default {
    name: 'register',
    data(){
        // 密码校验器
        const passwordValidator = (rule,value, callback) =>{
            console.log(rule)
            if(!validatePassword(value)){
                callback('密码强度不满足要求,密码必须同时包含字母、数字和特殊字符,请重新输入')
            } else {
                callback()
            }
        }
        // 二次密码校验器
        const password2Validator = (rule, value, callback) => {
            console.log(rule)
            const password = this.registerModel.password
            if(password!=value){
                callback(new Error('两次输入的密码不一致'))
            } else {
                callback()
            }
        }
        // 手机号码校验器
       const  phoneNumValidator = (rule, value, callback)=> {
             console.log(rule)
            if(!(value.length==11 && isNumber(value))){
                callback(new Error('手机号码必须是11位数字'))
            } else if(!validatePhoneNum(parseInt(value))){
                callback(new Error('手机号码不合法'))
            } else {
                callback()
            }
       }
       // 邮件地址校验器
       const emailValidator = (rule, value, callback) => {
          console.log(rule)
          if(value!='' && !validEmail(value)){
             callback(new Error('邮箱地址不合法'))
          } else {
            callback()
          }
       }
        // 区分本地开发环境和生产环境
       let uploadAvatarUrl = ''
       if(window.location.host='localhost'){
           uploadAvatarUrl = 'http://localhost:8081/blog/upload/user/avatar'
       } else {
          uploadAvatarUrl = 'http://www.javahsf.club:8081/blog/upload/user/avatar'
       }
        return {
            uploadAvatarUrl: uploadAvatarUrl,
            registerModel: {
                userAccount: '',
                nickName: '',
                password: '',
                password2: '',
                avatarSize: 32,
                uploadUrl: uploadUrl,
                phoneNum: '',
                email: ''
            },
            onLoading: false,
            avatarUrl: '',
            password2Style: {
                dispaly: 'none',
                color: 'red'
            },
            // 表单校验规则
            rules: {
                userAccount: [
                    { required: true, message: '请输入用户账号', trigger: 'blur' },
                    { min: 2, max: 64, message: '2-64个字符', trigger: 'blur' }
                ],
                nickName: [
                    { required: true, message: '请输入昵称',  trigger: 'blur' },
                    { min: 2, max: 64, message: '长度控制在2-64个字符',trigger: 'blur' }
                ],
                password: [
                    { required: true, message: '请输入密码', trigger: 'blur' },
                    { min: 6, max: 18, message: '长度控制在6-18个字符', trigger: 'blur' },
                    { validator: passwordValidator, trigger: 'blur' }
                ],
                password2: [
                    { required: true, message: '请再次输入密码', trigger: 'blur' },
                    { min: 6, max: 18, message: '长度控制在6-18个字符', trigger: 'blur' },
                    { validator: password2Validator, trigger: 'blur' }
                ],
                phoneNum: [
                    { required: true, message: '请输入手机号',  trigger: 'blur'},
                    { validator: phoneNumValidator, trigger: 'blur' }
                ],
                email: [
                    { min: 0, max: 64, message: '长度控制在64个字符'},
                    { validator: emailValidator, trigger: 'blur' }
                ]

            },
            redirect: undefined
        }
    },
    watch: {
        $route: {
            handler: function(route) {
                const query = route.query
                if (query) {
                this.redirect = query.redirect
                this.otherQuery = this.getOtherQuery(query)
                }
            },
            immediate: true
        }
   },
    methods: {
        // 图片上传之前校验图片格式和附件大小
        beforeAvartarUpload(file) {
           console.log(file)
           if(!(file.type=='image/jpeg' ||file.type=='image/png')){
              Message.error('头像图片必须是jpg或png格式')
           }else if(file.size/(1024*1024)>10){
              Message.error('图片大小不能超过10M')
           }
        },
        // 上传图片预览
        previewAvatar(file){
            console.log(file)
        },
        // 图片上传成功回调
        handleSuccessAvatar(response){
           console.log(response.data)
           this.avatarUrl = response.data
        },
        // 提交注册
        handleRegister(formName){
            this.$refs[formName].validate((valid=>{
                if(valid){ // 表单校验通过
                    const params = {
                        username: this.registerModel.userAccount,
                        nickname: this.registerModel.nickName,
                        password: this.registerModel.password,
                        phoneNum: this.registerModel.phoneNum,
                        email: this.registerModel.email,
                        userface: this.avatarUrl
                   }
                    this.onLoading = true
                    this.$store.dispatch('user/register', params).then(res=>{
                        this.onLoading = true
                        if(res.status===200){
                            Message.success('恭喜注册成功,现在就可以登录系统了!')
                            // 跳转到登录界面
                            this.$router.push({ path: '/login', query: this.otherQuery })
                        } else {
                            Message.error(res.msg)
                        }
                    })
                }else{  // 表单校验不通过,拒绝提交注册
                    this.onLoading = true
                    Message.error('用户注册信息校验不通过,请重新填写注册信息')
                    return false
                }
            }))
        },
        // 表单重置
        resetForm(formName) {
          this.$refs[formName].resetFields()
        },
        getOtherQuery(query) {
            return Object.keys(query).reduce((acc, cur) => {
                if (cur !== 'redirect') {
                acc[cur] = query[cur]
                }
                return acc
            }, {})
        }
    }
}
</script>
<!--页面样式-->
<style lang="scss" scoped>
    .register-container{
        margin-top: 100px;
        margin-left: 10%;
        .el-input{
            width: 60%;
        }
        .avatar-uploader .avatar{
            width: 240px;
            height: 240px;
        }
        .el-button.submit-btn{
            width: 10%;
            height: 40px;
            margin-left: 150px;
            margin-right: 25px;
        }
        .el-button.reset-btn{
            width: 10%;
            height: 40px;
        }
    }
</style>

4.2 工具类中增加校验方法

src/utils/validate.js中增加校验密码和手机号码的方法

export function validatePhoneNum(phoneNum) {
  const reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
  return reg.test(phoneNum)
}

export function validatePassword(password) {
  // 强密码:字母+数字+特殊字符
  const reg = /^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&*]+$)(?![\d!@#$%^&*]+$)[a-zA-Z\d!@#$%^&*]+$/
  return reg.test(password)
}

以上校验均使用正则表达式校验

4.3 API文件中添加用户注册方法

src/api/user.js文件中新增用户注册接口方法

export function register(data) {
  return request({
    url: '/user/reg',
    method: 'post',
    data
  })
}

4.4 全局方法中添加用户注册方法

src/store/modules/user.js 文件中的actions对象中增加用户注册行为方法

const actions = {
  // user register
  register({ commit }, registerInfo) {
    return new Promise((resolve, reject) => {
      register(registerInfo).then(response => {
        if (response.status === 200 && response.data.status === 200) {
          const resInfo = { status: response.status, msg: '注册成功' }
          resolve(resInfo)
        } else {
          const resInfo = { status: response.status, msg: response.data.msg }
          resolve(resInfo)
        }
      }).catch(error => {
        console.error(error)
        reject(error)
      })
    })
  },
    // ......省略其他已有方法
}

因为用户注册完之后需要跳转到登录界面,直接在注册页面调用后台用户注册接口成功后调用this.$router.push方法发现无法实现页面的跳转效果, 因此改为在vuex的全局dispatch中调用注册接口

4.5 路由列表中添加用户注册组件

src/router/index.js文件的固定路由列表中添加注册组件的路由

import Register from '@/views/register/index'

export const constantRoutes = [
  {
    id: '0',
    path: '/register',
    component: Register,
    hidden: true
  },
   //...... 省略其他路由
 ]

4.6 登录组件中添加用户注册的跳转链接

src/views/login/index.vue文件中的模板代码部分的登录按钮标签下面添加如下两行代码

<div>
   <router-link to="/resetPass" class="forget-password">忘记密码</router-link>
   <router-link class="register" to="/register">注册账号</router-link>
 </div>

同时对忘记密码注册账号两个链接添加样式(忘记密码功能尚待实现)

<style lang="scss" scoped>
    .register, .forget-password{
        width: 20%;
        height: 35px;
        color: blue;
        margin-right: 20px;
        cursor: pointer;
  }
</style>

4.7 路由跳转控制中添加白名单

在路由跳转控制文件src/permission.js文件中将注册用户的路由添加到白名单中

const whiteList = ['/login', '/register', '/auth-redirect'] // no redirect whitelist

如果不在白名单中加上用户注册的路由,你会发现在用户登录界面压根无法跳转到用户注册界面的

5 效果体验

在启动后端服务后,在vue-element-admin项目下通过 鼠标右键->git bash进入命令控制台

然后输入npm run dev 项目启动前端服务

然后在谷歌浏览器中输入:http://localhost:3000/回车进入登录界面

点击下面的【注册账号】链接就能跳转到用【用户注册】页面

填写好用户注册信息后就可以点击下面的【提交】按钮提交注册了,注册成功后系统会弹框提示用户中注册成功,并重新跳转到【用户登录】界面

6 写在最后

本文演示了在spring-boot项目中继承阿里云对象存储sdk实现了图片上传和用户提交登录两个接口的详细实现,同时前端使用element-ui库中的upload组件调用后端图片上传接口实现了附件上传功能,实现了一个完整的用户登录信息的校验和提交注册及注册成功后的页面跳转等功能。

相信对想要了解一个系统的用户模块是如何实现用户的注册以及注册成功后的页面跳转的完整功能的是如何实现的读者朋友一定会有所帮助的!

本文前后端项目代码git仓库地址如下,对源码感兴趣的读者朋友可以克隆到本地参考

blogserver项目gitee仓库地址

vue-element-admin项目gitee仓库地址

到此这篇关于SpringBoot项目整合Vue做一个完整的用户注册功能的文章就介绍到这了,更多相关SpringBoot项目整合Vue做一个完整的用户注册功能内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot项目整合注册功能模块开发实战

    目录 工程简介 准备工作: 第一步:注册功能的实现 1.1持久层的设计 1.2注册功能-业务层 1.3控制层 1.4前端页面的设计 注册功能实现完成 工程简介 准备工作: 项目所用到的html界面以及sql文件链接如下: 百度网盘获取地址: 链接: https://pan.baidu.com/s/1CcbQWP8RNqyy3ieib1osLg?pwd=t1qu 提取码: t1qu 第一步:注册功能的实现 1.1持久层的设计 1.1.1规范需要执行的SQL语句 1.用户的注册工作,相当于在做数据的

  • Android使用OKhttp3实现登录注册功能+springboot搭建后端的详细过程

    目录 一.Android前端实现 二.数据库 三.SpringBoot后端搭建 四.部署至服务器 五.运行测试 一.Android前端实现 新建一个login的项目,主要的几个文件在这里 1.gradle引入OKhttp3依赖 implementation 'com.squareup.okhttp3:okhttp:3.14.7' implementation 'com.squareup.okio:okio:1.17.5' 2.activity_main.xml布局文件 <?xml version

  • springboot+VUE实现登录注册

    本文实例为大家分享了springboot+VUE实现登录注册的具体代码,供大家参考,具体内容如下 一.springBoot 创建springBoot项目 分为三个包,分别为controller,service, dao以及resource目录下的xml文件. UserController.java package springbootmybatis.controller; import org.springframework.web.bind.annotation.CrossOrigin; im

  • springboot整合Dubbo与Feign的实现 (无注册中心)

    目录 一,SpringBoot 整合 Dubbo 1.1 服务提供者 1.1.1 核心依赖 1.1.2 核心配置 1.1.3 服务提供者代码结构 1.1.4 服务提供者暴露的API(DubboDemoServiceImpl) 1.1.5 服务提供者端的对象(User) 1.2 服务消费者 1.2.1 核心依赖 1.2.2 核心配置 1.2.3 服务消费者代码结构 1.2.4 服务消费者调用服务提供者 1.3 调用示例 1.3.1 消费者入口(测试是否导通) 1.3.2 消费者入口(测试携带参数与

  • SpringBoot整合Web之CORS支持与配置类和 XML配置及注册拦截器

    目录 本章概要 CORS 支持 1. 创建SpringBoot工程 2. 创建控制器 3. 配置跨域 4. 测试 配置类与 XML 配置 注册拦截器 本章概要 CORS 支持 配置类与 XML 配置 注册拦截器 CORS 支持 CORS (Cross-Origin Resource Sharing)是由 W3C 制定开发的一种跨域资源共享技术标准,其目的就是为了解决前端的跨域请求.在 Java EE 开发中,最常见的前端跨域请求解决方案是 JSONP ,但是 JSONP 只支持 GET 请求,而

  • 详解SpringBoot项目整合Vue做一个完整的用户注册功能

    目录 前言 1 实现用户注册流程 1.1 用户注册完整流程 1.2 用户注册信息及校验 2 后台接口设计 2.1 上传头像接口 2.2 用户注册接口 3 后端代码实现 3.1 用户头像上传接口编码实现 3.2 用户注册接口 4 前端代码实现 4.1 完成用户注册界面vue组件编码 4.2 工具类中增加校验方法 4.3 API文件中添加用户注册方法 4.4 全局方法中添加用户注册方法 4.5 路由列表中添加用户注册组件 4.6 登录组件中添加用户注册的跳转链接 4.7 路由跳转控制中添加白名单 5

  • 详解SpringBoot项目的创建与单元测试

    前言   Spring Boot 设计之初就是为了用最少的配置,以最快的速度来启动和运行 Spring 项目.Spring Boot使用特定的配置来构建生产就绪型的项目. Hello World 1.可以在 Spring Initializr上面添加,也可以手动在 pom.xml中添加如下代码∶ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>Spring-boot-s

  • 详解springboot项目带Tomcat和不带Tomcat的两种打包方式

    1,带Tomcat的打包方式 1.1, 在pom.xml文件添加以下配置(目的:自定main入口和跳过Junit代码) <build> <plugins> <!--打包为jar时指定main入口--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <

  • 详解SpringBoot项目docker环境运行时无限重启问题

    可能是我开始处理问题的思路不对,现在描述问题可能也有点乱,但是里面可能的处理方式希望能帮到遇到我这个坑的人 描述:springboot项目,docker镜像里面运行,看docker的日志,项目启动成功后,隔了一分钟左右他就自动重新启动,然后造成网站接口访问的时候nginx报502 gateway啥的,有两台服务器,一个是文件服务器,运行了很简单的上传下载文件的代码以及验证token,另一台运行了java应用,两台服务器都在一次更新项目的镜像,运行过后遇到了这个问题,很奇怪. 然后我将项目弄成ja

  • 详解Springboot之整合JDBCTemplate配置多数据源

    一.前言 现在在我们的项目中,使用多数据源已经是很常见的,下面,这里总结一下springboot整合jdbcTemplate配置多数据源的代码示例,以方便以后直接使用. 二.配置文件 spring: datasource: datasourceone: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/eesy?serverTimezone=UTC&characterEncoding=utf8&u

  • 详解如何用alpine镜像做一个最小的镜像并运行c++程序

    需求 工作中我们如果要制作镜像,一般都是直接pull官方镜像,比如我们要运行一个c++程序我们可能直接pull一个gcc,或者ubuntu镜像就可以了,但是存在一个问题,我们只是要运行一个c++程序却要运行一个ubuntu系统,这是非常消耗资源的,所以就去网上搜了搜发现早期的docker都是使用alpine镜像来做基础镜像,所以就用alpile镜像来制作镜像 dockerfile FROM alpine:3.7 MAINTAINER Rethink #更新Alpine的软件源为国内(清华大学)的

  • SpringBoot项目整合FastDFS+Nginx实现图片上传功能

    目录 FastDFS概述 Fastdfs原理 安装Fastdfs 整合Nginx模块 安装Nginx SpringBoot整合Fastdfs FastDFS概述 FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.特别适合以文件为载体的在线服务,如相册网站.视频网站等等. FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高性能等指标,使用Fa

  • 详解springboot项目docker部署实践

    网上查了下大部分boot项目部署都是在linux和docker hub环境下完成的,由于本人开发环境在windows下面,并且docker容器也是使用的docker tools,而且也没有做docker hub的加速.所以就尝试在windows环境下利用阿里云docker仓库完成部署. 1.在docker tools 中新建文件夹,并且找到位置,然后把打包好的boot jar文件放入新建的文件夹中 在该文件夹下面新建一个Dockerfile文件,示例如下: FROM daocloud.io/ja

  • 在Nginx服务器上安装SSL证书完成HTTPS请求的步骤详解(springboot项目)

    目录 步骤1:下载证书到本地 步骤2:(可选)在Nginx独立服务器上安装证书 http本博客是在我完成了http重定向https配置之后来总结的,如有问题请大家见谅!如有问题请评论留言!!! 阿里云文档地址:https://help.aliyun.com/document_detail/98728.htm?spm=a2c4g.11186623.2.7.550a7845ysZdw5#section-liy-o8x-gug 步骤1:下载证书到本地 1.登录SSL证书控制台. 2.在概览页面,单击证

  • 详解SpringBoot 快速整合Mybatis(去XML化+注解进阶)

    序言:使用MyBatis3提供的注解可以逐步取代XML,例如使用@Select注解直接编写SQL完成数据查询,使用@SelectProvider高级注解还可以编写动态SQL,以应对复杂的业务需求. 一. 基础注解 MyBatis 主要提供了以下CRUD注解: @Select @Insert @Update @Delete 增删改查占据了绝大部分的业务操作,掌握这些基础注解的使用是很必要的.例如下面这段代码无需XML即可完成数据查询: @Mapper public interface UserMa

随机推荐