前后端API的接入

简介: 对于开发一个 Web 项目来说,无论是电商还是其他品类的项目,注册与登录模块都是必不可少的

对于开发一个 Web 项目来说,无论是电商还是其他品类的项目,注册与登录模块都是必不可少的;


注册登录功能也是我们在日常生活中最长接触的,对于这个业务场景的需求与逻辑大概是没有什么需要详细介绍的,


市面上常见的邮箱注册、手机注册、账号密码注册,其处理方式基本相同,我们这里使用账号密码注册的方式,实现整个平台的注册 / 登录功能;



让我们开始!



判断用户名是否存在


       实现注册部分的代码,首先想到的就是,我们要对前端所发送过来的请求参数做验证,在有些项目中,会将请求参数的格式验证和合法性验证只写在前端校验,而后端只实现业务逻辑,我认为这是极其危险的编码习惯;当我们的项目放在线上的时候,就会有恶意用户绕过前端验证,直接访问我们的服务器,对线上业务造成破坏,因此前端验证是为了减轻一部分请求直接到达后端,但是相应的验证后端也要去做

   那么,首先我们要实现的就是验证用户注册的用户名是否存在;我们首先来看一下用户表的结构及设计:


    我们用户表以用户的 Id 为主键,但注意:ID 并不是自增长的,这与传统的 Id 设计不同,这里不是自增的原因是:当系统达到一定的体量时,用户数量激增,我们需要去做分布式集群,需要分库分表,这时自增的 ID 会给分库分表带来极大的困难,因为,出于日后系统优化的考虑,我们这里的数据库主键,不是自增的。



接口开发 你可能感兴趣的电商 API 接口商品关键字搜索 API

   理清业务逻辑,看完数据库结构,我们着手开始编写业务代码;在整个项目代码的编写和接口的实现我们都遵循自底向上的方式,从数据库开始,实现数据的映射,业务实现,结果推送的流程,对应 pojo 映射 ---Service 编写 ---Controller 控制的过程。

   那么,我们开始啦:首先,我们创建一个 UserService 接口,在接口中,我们编写我们第一个业务方法:

/**
 * 判断用户名是否存在
 */
 public boolean queryUsernameIsExist(String username);

我们传入一个 userName,返回一个布尔值;有了接口之后,我们去实现这个方法:

我们在 service 工程中,新建一个 Impl 的包,在里面新建一个类 UserServiceImpl,去实现 UserService 接口,并实现其中的方法;我们在这个方法中,需要操作 User 这个实体类,那么我们先把 UserMapper 引入进来:

@Autowired
public UsersMapper usersMapper;

在方法中,我们使用 Example 这种使用条件查询的方式去做查询:

@Transactional(propagation = Propagation.SUPPORTS)
@Override
public boolean queryUsernameIsExist(String username) {
    Example userExample = new Example(Users.class);
    Example.Criteria userCriteria = userExample.createCriteria();
    userCriteria.andEqualTo("username",username);
    Users result  = usersMapper.selectOneByExample(userExample);
    return result == null ? false : true;
}

在这个方法的实现中,我们使用了 Example 这种方式,Example 映射一个实体类,获得一个 example 对象,为这个对象去添加相应的条件,Criteria 对应的方法有很多,可以判断等于,大于,相似等各类条件,使用起来很方便,感兴趣的同学可以去阅读他的源码;这个方法返回一个 Users 对象,我们去判空,若为空,则用户名可用,若 false,则用户名存在;

   实现了 Service 之后,我们来编写 Controller,我们在 api 工程中,新建一个 Controller 类。命名为 PassportController,我们为他加上 RestController 注解,并加上路由地址,在这个 Controller 中,我们需要操作 UserService 来进行查询,那么,我们先将 UserService 注入进来:

@Autowired
private UserService userService;

并定义一个方法,声明方法的路由地址:

@GetMapping("/usernameIsExist")
public IMOOCJSONResult usernameIsExist(@RequestParam String username) {
    // 判断用户名不能为空
    if (StringUtils.isBlank(username)) {
        return IMOOCJSONResult.errorMsg ("用户名不能为空");
    }
    // 查找注册的用户名是否存在
    boolean isExist = userService.queryUsernameIsExist(username);
    if (isExist) {
        return IMOOCJSONResult.errorMsg ("用户名已经存在");
    }
    // 请求成功,用户名没有重复
    return IMOOCJSONResult.ok();
}

在这个方法中,我们在判断 username 为空的时候使用了一个字符串的工具类,他是 Apache 提供的,我们需要首先引入他的依赖:

<!-- apache 工具类 -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>1.3.2</version>
</dependency>

引入之后,我们便可以操作 StringUtils,他提供了一个 isBlank 的方法,来判断字符串是否为空;

   同时我们在方法中,可以看到一个 IMOOCJSONResult 对象,这是一个结果集对象,因为在代码中,我们需要返回的结果值类型很多,很不确定,并且,我们需要返回自定义的响应码来告诉前端请求成功与否,前端对约定好的各类返回值做统一处理,避免前端出现代码报错,影响用户体验,同时,自定义的结果响应能提高我们定位错误的速度,规范接口;因此我们先定义一个结果返回类:

public class IMOOCJSONResult {
    // 定义 jackson 对象
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 响应业务状态
    private Integer status;
    // 响应消息
    private String msg;
    // 响应中的数据
    private Object data;
    ...
}

完整的类代码请大家去看源码,不方便在这里粘贴全部代码,只提供类中的属性供大家参考,大家可以参考对应的实现思路去封装自己的结果集,我们使用这样统一的结果类便可以在返回数据的同时,返回请求状态码给前端,提高接口可读性和稳定性。

接口测试



   当我们第一个接口编写完成后,我们使用 PostMan 进行接口测试,postman 是一款开源免费的接口调试工具,可以模拟客户端发出请求,是后端开发必备工具:


我请求的是我的生产服务器接口,大家在本机调试的时候 URL 应该是 localhost 开头的,我们可以看到请求结果的响应也是我们封装的结果集的响应,有响应码和数据体组成,大家可以认识更换请求数据,测试接口功能的完整性。


用户注册


你可能感兴趣的电商 API 接口商品关键字搜索 API

完成了第一个判断用户名是否存在的接口后,我们着手开始进行用户注册的逻辑编写,首先我们在注册的时候,依然是操作 UserMapper,所以数据层是已经准备好的,我们在 UserService 中,定义第二个创建用户的方法:

/**
 * 创建用户
 * @param userBO
 * @return
 */
 public Users createUser(UserBO userBO);

这个方法会接收一个表单数据,数据包中包含密码,用户名,甚至更多的信息,如果我们在后端一个一个的接收,显然是不合理的,当请求参数过多时,我们便将请求参数封装成一个请求的实体类,在这里我们封装一个 UserBO:

public class UserBO {
    @ApiModelProperty (value = "用户名",name = "username",example ="张三",required =true)
    private String username;
    @ApiModelProperty (value = "密码",name = "password",example ="123456",required =true)
    private String password;
    @ApiModelProperty (value = "确认密码",name = "confirmPassword",example ="123456",required =false)
    private String confirmPassword;
    ...
}

get/set 方法大家自行生成,方法定义好后,我们去实现类中,实现他:

@Transactional(propagation = Propagation.REQUIRED)
@Override
public Users createUser(UserBO userBO) {
    // 使用工具类生成唯一 id
    String userId = sid.nextShort();
    Users user = new Users();
    user.setId(userId);
    user.setUsername(userBO.getUsername());
    try {
        user.setPassword(MD5Utils.getMD5Str(userBO.getPassword()));
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 默认用户昵称同用户名
    user.setNickname(userBO.getUsername());
    // 默认头像
    user.setFace(USER_FACE);
    // 默认生日
    user.setBirthday(DateUtil.stringToDate("1970-01-01"));
    // 设置性别 (使用枚举操作) 默认为:保密
    user.setSex(Sex.secret.type);
    user.setCreatedTime(new Date());
    user.setUpdatedTime(new Date());
    usersMapper.insert(user);
    return user;
}

我们在这里存储密码时,使用了 MD5 加密机制,防止数据库资源泄露,导致用户数据泄露,保证数据的安全性,MD5 的工具类大家可以在源码中获取,我就不贴在这里了;同时使用的工具类还有日期格式化工具类,同样的,大家在源码中获取;我们在新建用户设置性别时,我们可以使用枚举的形式去定义用户的性别,增强代码的可读性:

/**
 * @Desc:性别枚举
 */
public enum Sex {
    woman (0,"女"),
    man (1,"男"),
    secret (2,"保密");
    public final Integer type;
    public final String value;
    Sex(Integer type, String value) {
        this.type = type;
        this.value = value;
    }
}

我们在生成唯一 ID 的时候,我们也会使用工具类,注入 Sid 对象,使用 org.n3r.idworker 中的方法为了使用其他包中的方法,我们需要 SpringBoot 在启动时,扫描到 idworker,我们需要在 Application 中配置:

// 扫描所有包,以及相关组件包
@ComponentScan(basePackages = {"com.imooc","org.n3r.idworker"})
接口实现

写完 Service,我们回到 PassportController,因为是一个保存数据的方法,我们使用 Post 的方式:

@ApiOperation (value = "用户注册",notes ="用户注册",httpMethod = "POST")
@PostMapping("/regist")
public IMOOCJSONResult regist(@RequestBody UserBO userBO,
                              HttpServletRequest request,
                              HttpServletResponse response){
    String username = userBO.getUsername();
    String password = userBO.getPassword();
    String confirmPwd = userBO.getConfirmPassword();
    System.out.println(username);
    System.out.println(password);
    System.out.println(confirmPwd);
    // 判断用户名和密码必须不为空
    if (StringUtils.isBlank(username) ||
        StringUtils.isBlank(password) ||
        StringUtils.isBlank(confirmPwd)) {
        return IMOOCJSONResult.errorMsg ("用户名及密码不能为空");
    }
    // 查询用户名是否存在
    boolean isExist = userService.queryUsernameIsExist(username);
    if (isExist) {
        return IMOOCJSONResult.errorMsg ("用户名已经存在");
    }
    // 密码长度不能小于 6 位
    if (password.length() < 6) {
        return IMOOCJSONResult.errorMsg ("密码长度不能小于 6 位");
    }
    // 判断两次密码是否一致
    if (!password.equals(confirmPwd)) {
        return IMOOCJSONResult.errorMsg ("两次密码输入不一致");
    }
    // 实现注册
    Users userResult = userService.createUser(userBO);
    userResult = setNullProperty(userResult);
    CookieUtils.setCookie (request,response,"user",      // 使用 Cookie 工具类获取 Cookie
            JsonUtils.objectToJson (userResult),true);      // 使用 Json 工具类将对象转换为 String
    // TODO 生成用户 token,存入 redis 会话
    // TODO 同步购物车数据
    return IMOOCJSONResult.ok();
}

Controller 的实现的没有什么注重的点,我们接收到前端传来的数据包,解析数据包中的属性,进行校验,校验通过,调用 Service 层的方法,实现注册;同样的,对于这个方法,我们使用 PostMan 进行调试。

推荐

No.1


Swagger2

你可能感兴趣的电商 API 接口商品关键字搜索 API

在上面的部分,我们实现了两个接口,实现了注册的业务功能,并使用 PostMan 进行了接口调试,这些都属于后端开发的部分,那么我们如何在前后端分离的模式下,与前端沟通联调呢,这时候我们需要使用接口文档:接口文档规范了接口路由、参数,入参、出参以及参数的类型。但是对于程序员来说,花费大量的时间去编写文档,是不科学不可取不合理的,那么 Swagger2 应运而生!首先,我们引入依赖:

<!-- swagger2 配置 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.4.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.4.0</version>
</dependency>
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>swagger-bootstrap-ui</artifactId>
    <version>1.6</version>
</dependency>

引入依赖后,我们需要定义配置类,我们在 api 工程汇总创建 Swagger2 的配置类:

@Configuration
@EnableSwagger2
public class Swagger2 {
//    http://localhost:8088/swagger-ui.html         原路径
//    http://localhost:8088/doc.html
    // 配置 swagger2 核心配置 docket
    @Bean
    public Docket createRestApi(){
        return new Docket (DocumentationType.SWAGGER_2)// 指定 api 类型为 swagger2
                .apiInfo (apiInfo ())                   // 用于定义 api 文档汇总信息
                .select().apis(RequestHandlerSelectors
                        .basePackage ("com.imooc.controller"))   // 指定 controller 包
                .paths (PathSelectors.any ())           // 所有 Controller
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title ("天天吃货    电商平台接口 api")     // 文档页标题
                .contact(new Contact("eumen",
                        "https://www.imooc.com",
                        "18829526908@163.com"))         // 联系人信息
                .description ("api 文档")                 // 详细信息
                .version ("1.0.1")                       // 文档版本号
                .termsOfServiceUrl ("106.54.54.200")// 网站地址
                .build();
    }
}

配置完成后,我们便可以使用 API 这个注解去添加方法的参数说明,和方法说明了,并且,启动项目,访问 BASE_URL/Swagger-ui.html 便可以看到生成的项目 API 文档:



New Arrival



对于 Swagger2 的使用,大家可以去查阅文档,这是一个非常好用的文档生成工具,提高生产力,让开发远离文档困扰!我们甚至可以在文档里做接口的调试:

相关文章
|
6月前
|
缓存 JavaScript Java
使用 API 网关
使用 API 网关
109 0
|
XML JSON 缓存
vvic API 接入说明
vvic API 接入说明
|
运维 监控 负载均衡
公司为什么都有API网关?聊聊API网关的作用
1、Open API 企业需要将自身数据、能力等作为开发平台向外开放,通常会以rest的方式向外提供。最好的例子就是淘宝开放平台、腾讯公司的QQ开发平台、微信开放平台。 Open API开放平台必然涉及到客户
公司为什么都有API网关?聊聊API网关的作用
|
存储 弹性计算 监控
谈API网关
API网关的意义和常用选择
457 0
谈API网关
|
缓存 运维 监控
API 网关要做很多工作
通常情况下, API 网关要做很多工作
290 0
|
存储 监控 BI
1分钟构建API网关日志解决方案
访问日志(Acccess Log)是由web服务生成的日志,每一次api请求都对应一条访问记录,内容包括调用者IP、请求的URL、响应延迟、返回状态码、请求和响应字节数等重要信息。 阿里云API网关提供API托管服务,在微服务聚合、前后端分离、系统集成上为用户便利的产品。
9318 0
|
设计模式 缓存 负载均衡
|
前端开发 安全 Cloud Native
最终,我选择了 GraphQL 作为企业 API 网关
最近,我参与了一个从头开始构建新应用程序的项目,该项目是一个拥有许多前端用户的大型企业级业务应用程序。为了实现所需的逻辑和功能,该项目的架构设计中需要构建大约 50 个微服务,其中一些微服务需要原生地部署到云上,而另一些则需要托管在本地的 OpenShift 集群中,且 OpenShift 集群将成为与遗留数据系统的连接纽带。
Mac
|
API 微服务 负载均衡
API网关
网关解决的问题: 1、对外API接口暴露统一和隐藏后台微服务 2、统一鉴权 3、限流 Zuul与GateWay
Mac
1217 0