测试开发【Mock平台】04实战:前后端项目初始化与登录鉴权实现


【Mock平台】为系列测试开发教程,从0到1编码带你一步步使用Spring Boot 和 Antd React
框架完成搭建一个测试工具平台,希望作为一个实战项目能为你的测试开发学习有帮助。

一、后端SpringBoot

参考之前《》分享来创建后端的服务,实际上QMock服务会涉及到两个服务,一个是供前端页面用的API服务,另一个是mock请求响应服务即可叫其网关,为了统一管理代码又不都耦合到一块,本项目通过IDE先创建一个普通的JAVA项目叫 QMockService,然后再其项目中创建两个**Module Springboot **项目,服务名分别为:

  • qmock-service-api
  • qmock-servcie-gateway

由于第二个代理网关服务暂时用不到,所以你也可以只单独创建一个service-api项目用于实践学习。
在这里插入图片描述

API服务架构

通常一个项目都会有约定俗成的模式来规范开发,由于对于JAVA的模式太多,我这里只提供基于MVC模式扩展的我常用结构供参考, 详见qmock-service-api左侧在 main.java包下子目录。

java
|- cn.daqi.mock.api  # 代码包
   |- commmons       # 通用或工具类
   |- controller     # 接口请求入口
   |- entity         # 数据表实体类
      |- request     # 接口请求实体
   |- mapper         # 数据操作接口类
   |- service        # 服务接口类
      |- impl        # 服务接口实现类
resources
|- mapper            # Mybatis XML方式数据操作文件
|- application.yml   # 项目配置采用yml方式

由于笔者的职业不是后端开发,对于代码架构和模式等,没有过度的实战经验,如果想对各种模式有更多的了解推荐阅读之前转载过美团技术的一篇文章,如果想更深入的了解建议买本架构、代码之道之类的书进行系统学习。

https://d.umijs.org/zh-CN),在开发环境下会展示,主要方便使用文档相关的记录,相当于一个内部Wiki,个人觉得还比较有用,QMock后边的一些相关说明信息也打算放在这里。
在这里插入图片描述
经过精简优化后看下最终效果
在这里插入图片描述

三、登录功能实现

上边说了很多基础配置相关的,接下来个实战打通前后端服务,实现登录功能。

说明:以下实现主要照着做即可,不用勉强看懂每个实现,后续的分享具体应用到会逐一的讲,如果太过在意会打击学习的积极性,当然如果后边没有讲到或者不够清晰也欢迎加互相探讨。

用户表创建

数据库使用的是Mysql5.7+版本,本项目创建名为qmock的数据库,并创建一个users的用户表,同时添加两条数据,SQL语句如下:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT '',
  `password` varchar(50) DEFAULT '',
  `access` varchar(20) DEFAULT 'gust',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
INSERT INTO `users` VALUES (1, 'admin', 'admin', 'admin');
INSERT INTO `users` VALUES (2, 'user', 'user', 'user');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

登录接口

还记得一开始我们添加一些依赖吗?如果要实现Mybtis的数据库操作还需要在application.yml 增加一些配置。

server:
port: 8081 # 服务启动端口

# 数据库链接信息
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/qmock
username: mrzcode
password: mrzcode
driver-class-name: com.mysql.jdbc.Driver

# Mybatis基本配置
mybatis:
type-aliases-package: cn.daqi.mock.api.entity # 指定实体类所在包
mapper-locations: classpath:mapper/*.xml # 指定mapper xml 所在位置
configuration:
    map-underscore-to-camel-case: true  # 数据库表字段自动转驼峰命名 如:user_name -> userName

抓取 Antd pro登录时候请求mock接口的路径和参数

curl 'http://localhost:8000/api/login/account' \
  --data-raw '{"username":"admin","password":"admin","autoLogin":true,"type":"account"}' 

根据此文开头给出的API请求流程图实现每一个对应的类,这里从里层往外层逐步给出代码

(一)根据用户名和密码匹配查询 注解为 @Mapper

package cn.daqi.mock.api.mapper;

import org.apache.ibatis.annotations.*;

@Mapper
public interface LoginMapper {

    @Select("SELECT count(*) FROM users WHERE name=#{name} and `password`=#{password}")
    Integer userLogin(@Param("name") String name, @Param("password") String password);
}

(二)定义请求参数 LoginRequest.java 请求参数类 lombok @Data 注解,其中SQL只判断是否查询到用户,所以暂时用不到 LoginEntity.java 这里便不罗列了。

package cn.daqi.mock.api.entity.requests;

import lombok.Data;

@Data
public class LoginRequest {
    private String username;
    private String password;
}

(三)登录服务Interface和class实现类

package cn.daqi.mock.api.service;
// ...省略import,自动添加或详细看源代码
public interface LoginService {
    RespResult accountLogin(LoginRequest req);
}

注解@Service 放在实现类上

package cn.daqi.mock.api.service.impl;
// ...省略import,自动添加或详细看源代码
@Service
public class LoginImpl implements LoginService {

    @Autowired
    LoginMapper loginMapper;

    @Override
    public RespResult accountLogin(LoginRequest req) {
        Integer count= loginMapper.userLogin(req.getUsername(), req.getPassword());
        if (count > 0) {
            return RespResult.success();
        } else {
            return RespResult.failure(RespCode.USER_AUTHORITY_FAILURE);
        }
    }
}

(四)登录API实现类 注解说明

  • @RestController 声明为控制器(= @Controller + @ResponseBody)
  • @RequestMapping 定义跟路径
  • @PostMapping 定义POST请求方法和子路径
package cn.daqi.mock.api.controller;

import cn.daqi.mock.api.commons.RespResult;
import cn.daqi.mock.api.entity.requests.LoginRequest;
import cn.daqi.mock.api.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ Author: Zhang Qi
 * @ Copyright: 博客&公众号《大奇测试开发》
 * @ Describe: 登录接口API
 */
@RestController
@RequestMapping(value = "/api/login")
public class LoginController {

    @Autowired
    LoginService loginService;

    /**
     * 登录验证接口
     * @param req Post请求body参数体
     * @return JSON统一格式体
     */
    @PostMapping(value = "/account")
    public RespResult login(@RequestBody LoginRequest req){
        return loginService.accountLogin(req);
    }
}

(五)接口测试
运行服务分别用存在和不匹配用户密码进行下接口请求测试
在这里插入图片描述

登录页面

完成了后端的用户鉴权接口,现在来改造下前端的登录,使其此接口从mock请求切换到真正的后端请求,涉及以下几个处。

(一) 代理转发 同上个vue系列一样,前后端的分离项目为了解决跨域的问题都需要配置下proxy,项目使其转发指向本地的qmock-service-api后端服务,修改的文件为 config/proxy.js

dev: {
    // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
    '/api/': {
      // 要代理的地址
+      target: 'http://localhost:8081',
-     // target: 'https://preview.pro.ant.design',
      // 配置了这个可以从 http 代理到 https
      // 依赖 origin 的功能可能需要这个,比如 cookie
      changeOrigin: true,
    },
  },

(二)去掉前端Mock登录配置 位于mock/user.js 注释或者删除掉整块mock接口定义

'POST /api/login/account': async (req, res) => {
  // ...省略内部代码
}

由于我们只是替换了一个登录接口,其他如用户信息等没有实现,依然走的是mock,所以antd这里会坑需要同步注意修改!

GET /api/currentUser 这个Mock方法需要注释或删除掉 if (!getAccess()) { ...省略... }
部分代码,否则会验证鉴权失败,大家可以打开debug对比试试。

(三)修改/account 请求 默认登录接口和后端的统一接口格式不一样,这里需要稍微修改前端对其接口的逻辑判断,登录页面文件位于 src/pages/user/Login/index.jsx

const handleSubmit = async (values) => {
    try {
      // 登录
      const msg = await login({ ...values, type });

+      if (msg.success) {
-      // if (msg.stutus) {
        const defaultLoginSuccessMessage = intl.formatMessage({
          id: 'pages.login.success',
          defaultMessage: '登录成功!',
        });
        message.success(defaultLoginSuccessMessage);
        await fetchUserInfo();
        /** 此方法会跳转到 redirect 参数所在的位置 */

        if (!history) return;
        const { query } = history.location;
        const { redirect } = query;
        history.push(redirect || '/');
        return;
      }

      console.log(msg); // 如果失败去设置用户错误信息

+      setUserLoginState({ status: 'error', type:'account' });
-     // setUserLoginState(msg)
    } catch (error) {
      const defaultLoginFailureMessage = intl.formatMessage({
        id: 'pages.login.failure',
        defaultMessage: '登录失败,请重试!',
      });
      message.error(defaultLoginFailureMessage);
    }
  };

以上如果全部顺利弄完,重新启动前后端服务来联调看下效果吧,如图登录的接口正确请求了真实的接口。
在这里插入图片描述

本次分享内容稍微有点多,时间也拖的有点久,主要是一些内容笔者在给大家实战中也有学习成本和各种问题,好在功夫不负有心人,希望通过的我的前期天坑能让大家在学习少一些弯路。

最后笔者在学习Antd中有一点体会是,React确实比Vue入门使用要复杂些,但花了两天时间看了下官方文档后更加觉得React和Antdpro在支持平台全栈开发更能有好多表现。后边也打算随着我自己掌握技能的深入,然后出一个从测试开发角度理解的React基础教程,这样对于用好Antd更事半功倍。

本次代码已同步更新到GitHub上,有需要关注并回复 “mock平台”获取,同时本次内容作为一个模版单独打了temple分支,方便大家参考或直接使用,但后续的功能实现都会正常以master分支提交。