少湖说 | 科技自媒体

互联网,科技,数码,鸿蒙

概述

在之前的文章Golang持续集成与自动化测试和部署
简要介绍了如何快速搭建Api 微服务, 并进行DevOps集成。本文将进一步深入介绍,如果使用Gin实现Restful规范并进行自动化测试。

Restful

RESTful接口规范一文中,介绍了 Restful 及其规范。

Gin 是一个性能优异的微服务框架,对于Restful有着良好的支持。因为基于此实现Restful并不困难。

Restful 中的核心在于对于资源的定义和使用,和对 HTTP 动词(GET, PUT, DELETE, POST, DELETE, OPTIONS)的充分使用,通过 HTTP 动词,免去了繁冗的接口方法定义。
例如,传统方式如果实现删除文章功能,则需要实现一个类似 post/delete?id=6 的方法, 如果使用 Restful, 则使用 DELETE post/1 形式,其中 DELETE是动词,代表删除操作,
post/1 代表单个文章资源。

Restful 中的另一大规法是对于状态码的充分使用,不再使用响应内容表明状态。如200代表成功,404代表资源不存在,204代表删除成功,201代表创建成功。
有了状态码,响应内容中不再需要使用单独的字段来标记结果的状态(如 {result: “success”} )。

这样的一大好处是实现方式更规范、统一和直观。只要知道资源定义(posts), 就可以猜测出该如何进行增删改查的操作,不再需要知道方法名。
如此一来,可以很轻松的封装出一套同意的方法和操作。

路由定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

package router

import (
"os"
"gitlab.com/zacksleo/project/controllers"
"github.com/gin-gonic/gin"
)

api.GET("/customers", controllers.GetCustomers)
api.GET("/customers/:id", controllers.GetCustomer)
api.POST("/customers", controllers.CreateCustomer)
api.PUT("/customers/:id", controllers.UpdateCustomer)
api.DELETE("/customers/:id", controllers.DeleteCustomer)

下面一个较为完整的案例 controllers/customer.go

自动化测试

当实现了接口发后,为了验证接口运行正常,确保接口可靠和可用性,建议你对接口进行HTTP测试。
这里使用 httpexpect 对api进行测试,该库对响应的数据类型和结构验证有着较好的支持。

概述

前面分别介绍了 Golang 持续集成与自动化测试和部署使用Gin开发Restful接口并进行自动化测试
那么Restful接口搭建好了以后,如何进行接口授权验证,本文将讲述这些内容。

go-oauth2

Oauth2 是一种验证授权机制,通过下发令牌,来实现接口的认证。

go-oauth2

路由

1
2
3
4
auth := r.Group("/api/v1/oauth2")
{
auth.POST("/tokens", controllers.CreateToken)
}

控制器实现

获取Token的测试

这里为了方便在其他接口测试案例中调用获取token的方法,特意进行了拆分

其他接口的测试

概述

Golang是一门性能优异的静态类型语言,但因其奇快的编译速度,结合DevOps, 使得它也非常适合快速开发和迭代。

本文讲述如何使用Golang, 进行持续集成与自动化测试和部署。主要使用了以下相关技术:

  • dep: 进行包的依赖管理
  • gin: 搭建 api 服务
  • gorm:ORM, 数据CRUD
  • mysql: 存储数据
  • testfixtures: 测试夹具,在自动化测试时,自动向数据库填充用于测试的数据
  • httpexpect: HTTP 测试包,用于API测试
  • GoDotEnv: 环境变量处理
  • go test: 使用test命令进行单元测试, 基准测试和 HTTP 测试
  • GitLabCI: DevOps 工具
  • golint: Golang 静态检查工具
  • migrate: 数据库迁移工具
  • Docker: 使用 zacksleo/golang 镜像, 该镜像默认安装了 curl,git,build-base,dep 和 golint
  • db2struct: 将数据库表结构一键生成为 struct(gorm的model)
  • apig: 基于 gorm 和 gin 一键生成 CRUD API

开发流程

  • 使用 apig 脚手架工具初始化项目结构和目录
  • 使用 dep 安装相关依赖
  • 使用 migrate 编写数据库迁移方法,并执行迁移创建数据表
  • 使用 db2struct 生成 models
  • 使用 apig 生成 crud 代码
  • 使用 httpexpect 编写 api 测试代码,并通过 testfixtures 实现数据的自动填充
  • 编写 GitLabCI 脚本进行持续集成

在上述过程中,如需连接数据库时,可通过 GoDotEnv 来实现环境变量的使用

相关CI脚本

集成测试

注意事项

在测试中,如果需要区分单元测试和集成测试,可以使用 build tags 实现,如在文件头部中添加 // +build integration, 运行测试使用 - go test -tags=integration $(go list ./tests/... | grep -v /vendor/) -v 可以只执行集成测试

参考文档

REST接口文档遵循 API Blueprint 编码规范

中文说明需要遵循 中文文案排版指北

按照该格式书写的文档, 可以被 GitLab 及 Gollum 完美解析

主要内容

  1. 使用 Markdown 编写
  2. 请求和响应主体, 使用8个字符缩进
  3. 不使用` 包裹json代码

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

## 用户密码 [/users/password]

### 重置密码 [PATCH]


+ Attributes
+ phone (number, required) - 手机号码
+ password (string, required) - 密码
+ verify_code (string, required) - 短信验证码

+ Header
+ Authorization Bearer: {clientToken} - 访问令牌, 通过客户端模式获得
+ Content-Type: application/json

+ Request (application/json)

{
"phone":"17083300514",
"password":"pa66w0rd",
"verify_code":"312338"
}

+ Response 201 (application/json)

{
"phone":"17083300514",
"password":"pa66w0rd",
"verify_code":"312338"
}

+ Response 422 (application/json)

[
{
"field": "verify_code",
"message": "短信验证码错误"
},
{
"field": "phone",
"message": "手机号不存在"
},
{
"field": "password",
"message": "密码至少包含一位数字"
}
]



## 扫描二维码 [/qrcode-tokens/{ticket}]

### 更新状态 [PUT]

+ Attributes
+ action (string, required) - 动作, 值为SCANNED或LOGGED_IN
+ ticket (string, required) - ticket 二维码中链接中后面的字符串

+ Header
+ Authorization: Bearer {clientToken}
+ Content-Type: application/json

+ Request (application/json)

{
"action":"SCANNED"
}

+ Response 201 (application/json)

{
"action":"SCANNED"
}


参考链接

流程概要

持续集成和持续交付是 DevOps 最核心的两个部分。

持续集成通过即时将最新的代码,集成到主干分支,并进行相关的测试(单元测试、集成测试等)和静态检查(代码格式,代码质量等),以期提早发现问题。

持续交付,在持续集成完成之后,即时生成生产环境可用的产物(如二进制文件、包、或者 Docker镜像),并准备随时部署,如果伴随着部署过程,则称为持续部署。

开发流程

  • 系统分析与设计:需求分析,架构设计,数据库设计等

  • 相关文档编写, 文档应与代码仓库一起

  • 系统开发

    • 持续集成 (gitlab-ci)
    • 格式检查和静态检查 (vscode, linter)
    • 数据库迁移 (migration)
    • 使用 Docker 搭建开发、测试和生产环境,docker-compose
    • Git开发流程 (git cz, master, branch )
    • 自动化测试:单元测试,HTTP测试(api测试),功能测试,基准测试 (ab)
    • 自动化部署(主备)(ngixn back)
    • 更新日志 (angular changelog)
    • crontab supervisord
  • 测试与验收

参考文档

  1. 删除旧版
1
sudo yum remove docker docker-common  docker-selinux  docker-engine
  1. 安装库
1
sudo yum install -y yum-utils  device-mapper-persistent-data  lvm2
  1. 配置stable repo
1
sudo yum-config-manager --add-repo  https://download.docker.com/linux/centos/docker-ce.repo
  1. 安装
1
sudo yum install docker-ce
  1. 启动
1
sudo systemctl start docker
  1. 开机启动
1
sudo systemctl enable docker

7 . hello world

1
sudo docker run hello-world

简述

本文讲述了如何使用 GitLab-CI 对 ReactNative 项目,自动打包和部署,由于安卓环境更易于在Docker上搭建环境,所以先实现了安卓应用的打包和部署。

环境依赖

  1. Docker环境使用 registry.gitlab.com/wldhx/gitlab-ci-react-native-android:master 镜像, 该镜像包含了 ReactNative 打包安卓应用所需要的所有依赖
  2. 注册一个 fir.im 账号
  3. Python3。 部署应用采用的 fir.im 平台发布,因此实现了一段用于自动上传 apk 的 Python 脚本
  4. Node。 用于安卓依赖和格式化检查

编写GitLab-CI 脚本

$ cat .gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
image: node

stages:
- test
- build
- deploy

cache:
key: ${CI_PROJECT_ID}
paths:
- android/.gradle/

variables:
NPM_CONFIG_CACHE: "/cache/npm"
YARN_CACHE_FOLDER: "/cache/yarn"
DOCKER_DRIVER: overlay2
lint:
stage: test
dependencies: []
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- node_modules
script:
- if [ ! -d "node_modules" ]; then
- yarn install --cache-folder /cache/yarn
- fi
- yarn run lint
except:
- master
- tags
tags:
- docker

build-package:
stage: test
dependencies: []
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- node_modules
script:
- yarn install
artifacts:
name: "node_modules"
untracked: false
expire_in: 60 mins
paths:
- node_modules
only:
- master
- tags
tags:
- docker

build-android:
image: registry.gitlab.com/wldhx/gitlab-ci-react-native-android:master
stage: build
dependencies:
- build-package
script:
- cd android && ./gradlew assembleRelease --no-daemon
artifacts:
expire_in: 7 days
paths:
- android/app/build/outputs/apk/
only:
- master
- tags

release-android:
image: python:3
stage: deploy
cache: {}
dependencies:
- build-android
script:
- pip install -r requirements.txt
- if [[ -z "$CI_COMMIT_TAG" ]];then
- echo $(git log -3 --pretty=%s | tail -1) > ./release.txt
- else
- echo $(git tag -l --format='%(contents)' $CI_COMMIT_TAG) > ./release.txt
- fi
- python ./upload.py
only:
- master
- tags

该脚本分为三个阶段,第一阶段进行格式化检查,和安装 npm 依赖,第二阶段用于构建 apk, 第三阶段将apk上传到 fir.im 平台上,
其中 release.txt 用于存储最近的更新日志,便于在上传 apk 时,加上对应信息。

requirement.txt 文件中则使用了request库:

$ cat requirement.txt

1
requests

upload.py 文件则调用fir.im的上传API, 则 apk 上传到改平台,需要注意的时,api_token 需要在 fir.im 平台的右上角个人信息处获取

$ cat upload.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

import requests
import os


def file_get_contents(filename):
with open(filename) as f:
return f.read()


r = requests.post(
'https://api.fir.im/apps',
data={'type': 'android',
'bundle_id': 'com.company.package',
'api_token': '个人的API TOKEN'
}
)

data = r.json()

if os.environ.get('CI_COMMIT_TAG') is None:
tag = 'latest'
else:
tag = os.environ['CI_COMMIT_TAG']

changelog = file_get_contents('./release.txt')

files = {'file': open(
'./android/app/build/outputs/apk/release/app-release.apk', 'rb')}
r2 = requests.post(
data['cert']['binary']['upload_url'],
data={
'key': data['cert']['binary']['key'],
'token': data['cert']['binary']['token'],
'x:name': '应用名称',
'x:version': tag,
'x:build': 'com.company.package',
'x:changelog': changelog
},
files=files
)

print(r2.json())

参考内容

使用

  • 手机号参数对11位大陆手机号兼容
  • 支持国际手机号,请使用形如(加号)(地区编码)(一个空格)(手机号)的手机格式,例如
地区 合法格式
中国大陆 18682300424
中国大陆 +86 18682300424
美国 +1 2067925640
加拿大 +1 12042345678
英国 +44 7400123456
澳大利亚 +61 412345678

所以手机格式为:以加号开头,前面是地区区号,后面是手机号码,中间保留一个空格

  • 如果国际手机号出现在URL中,请对手机号进行编码(urlencode)

存储和处理手机号码

  • 地区编号和手机号应当分开存储
  • 增加对原11位手机号的兼容性(例如默认自动添加+86前缀)
  • 存储用户信息时,需要将phonecountry_code两个信息分别存储

验证和格式化手机号

  • 使用类库https://github.com/googlei18n/libphonenumber 或者其他语言的实现: https://github.com/googlei18n/libphonenumber#third-party-ports

使用国际手机号测试

  • 搜索可以在线接收短信验证码的平台
  • 在线接收短信验证码,进行后续测试
  • 验证码接收网站参考网址:
  • http://receive-sms.com/
  • https://tieba.baidu.com/p/3752380669
  • http://uuxn.com/seven-recerve-sms-online-free-phone
  • http://www.mfwu.net/receive-sms.html

简介

Kong 是一个微服务API网关。

Kong是一个云原生,快速,可扩展和分布式微服务抽象层(也称为API网关,API中间件或在某些情况下为Service Mesh)。
作为2015年的开源项目,其核心价值在于高性能和可扩展性。
Kong积极维护,广泛应用于从创业公司到Global 5000以及政府组织等公司的生产。

Konga 是一个用于管理网关Kong的管理端,通过它可以方便的进网关进行管理配置。

使用网关能解决很多问题:

  1. 解决端口和域名问题,代理后消除端口,将域名映射到端口,将服务映射成目录
  2. 微服务代理,将微服务置于内网,统一由网关代理
  3. 授权,可以配置授权管理,主要用于API授权
  4. 负载均衡,可以增强高访问量下的可用性,解决部署时的服务中断问题

本文主要讲述使用 Konga 对 Kong 进行网关管理配置

安装

安装过程具体请参考官网文档,如果使用docker安装 Kong 和 Konga,可以参考 配置docker-compose

配置反向代理

在 APIS 一栏,点击 ADD NEW API 按钮,添加一个代理:

  • 其中,Name 为显示名称,可任意填写;
  • Hosts 为所要使用的域名,如果不填写,则使用网关绑定的域名;如果填写,则可通过该域名访问;
  • Uris 为访问路径,如果需要将某个服务映射为一个目录,则此处需要配置;
  • Upstream URL 为上游地址,即微服务实际地址,另外可将微服务置于内网,此处即为内网地址。

需要注意的是,如果页面中有301/302跳转,需要将 Preserve Host 勾选,以保证跳转后,header中携带的 Location 中的域名为代理后的域名,否则会出现实际域名/内网域名,造成混乱,甚至暴露微服务地址

示例一:普通反向代理

配置项 内容 说明
Name dashboard 只是为了方便识别
Hosts dashboard.xxx.com 绑定的域名,类似于vhosts
Uris / 绑定目录
Methods 请求方法,默认不填
Upstream URL http://192.168.0.2:8080 实际微服务地址,建议使用内网ip, 并将该服务屏蔽外网访问
Strip uri YES
Preserve Host YES 转发时保留域名,处理301问题
Https only YES 如果不使用https,不勾选

配置负载均衡

需要注意的是,如果要使用负载均衡,需要配置 上游 (UPSTREAMS)。

在 UPSTREAMS 一栏,点击添加,Name 为一个域名形式的上游名称,如 dashboard.upstream.xxx.com, 添加完后,点击详情里面的Targets,添加一个目标,
Target 为实际的微服务地址,如 192.168.0.1:8080, 注意这里不写http协议,只写ip或域名。

一个 UPSTREAMS 可以配置多个 Targets, 针对每个 Targets 设置不同的 Weight,即实现了负载均衡。

示例二:负载均衡

UPSTREAMS 配置

配置项 内容 说明
Name coupon.api.foundation.com 类域名的别名

Targets 配置

配置项 内容 说明
Target 192.168.0.2:8001 微服务1的地址(建议使用内网)
WEIGHT 100 权重

Targets2 配置

配置项 内容 说明
Target 192.168.0.3:8001 微服务1的地址
WEIGHT 100 权重

Apis 配置

配置项 内容 说明
Name coupon 名称,任意
Hosts 留空使用网关默认的域名,如 api.xxx.com
Uris /coupon 通过 api.xxx.com/coupon 访问该服务
Methods 留空不限制
Upstream URL http://coupon.api.foundation.com/api/ 这里为配置的 UPSTREAMS 里的 Name
Strip uri YES
Preserve Host YES
Https only YES 如果不使用https,不勾选

参考文档

使用Taro和Typescript进行小程序开发

使用指南

在使用 taro 生成 Typescript 模板以后,需要做以下修改:

配置 .eslintrc

  • 加入 "no-undef": 0, 以解决变量undefined的问题
  • 加入 "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }] 以解决 JSX not allowed in files with extension '.tsx

增加配置文件

  • tsconfig.json
  • tslint 文件

增加依赖库

  • yarn add –dev tslint
  • yarn add –dev tslint-react
  • yarn add –dev ptypescript-eslint-parser

开始使用

1
2
3

yarn install

代码静态检查

1
2
yarn run lint

参考文档

0%