简单 web 服务与客户端开发实战

任务目标
选择合适的 API 风格,实现从接口或资源(领域)建模,到 API 设计的过程
使用 API 工具,编制 API 描述文件,编译生成服务器、客户端原型
使用 Github 建立一个组织,通过 API 文档,实现 客户端项目 与 RESTful 服务项目同步开发
使用 API 设计工具提供 Mock 服务,两个团队独立测试 API
使用 travis 测试相关模块

项目地址

资源来源

我的博客。
使用CSDN博客导出工具
在这里插入图片描述

前端

模仿rest v3 api风格设计api,有users,acticles, reviews, tags四种资源。

"GetArticleById":"/v3/article/{id}",
    "GetArticles":"/v3/articles",
    "GetCommentsOfArticle":"/v3/article/{id}/comments",
    "CreateComment":"/v3/article/{id}/comment",
    "SignIn":"/v3/auth/signin",
    "SignUp": "/v3/auth/signup"

使用swagger在线编辑器,可以即时响应,使用yaml语法编写API文档。

swagger: "2.0"
info:
  description: "A Simple Blog"
  version: "1.0.0"
  title: "Swagger Blog"
  termsOfService: "http://swagger.io/terms/"
  contact:
    email: "apiteam@swagger.io"
  license:
    name: "Apache 2.0"
    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "blog.swagger.io"
basePath: "/v3"
tags:
- name: "article"
  description: "Everything about users' articles"
- name: "user"
  description: "Operations about user"
schemes:
- "https"
paths:
  /articles:
    get:
      tags:
      - "article"
      summary: "Get articles"
      description: "Get the title of the request page of articles"
      operationId: "GetArticles"
      produces:
      - "application/json"
      parameters:
      - name: "page"
        in: "query"
        description: "the request page"
        required: true
        type: "string"
      responses:
        200:
          description: "Successful Operation"
          schema:
            $ref: "#/definitions/ArticlesResponse"
        404:
          description: "Not Found"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "User Not Exists"
                - "Article Not Exists"
  /article/{id}:
    get:
      tags:
      - "article"
      summary: "Get article by id"
      description: "Get an article by it's id"
      operationId: "GetArticleById"
      produces:
      - "application/json"
      parameters:
      - name: "id"
        in: "path"
        description: "The only Id of the article for the filter"
        required: true
        type: "integer"
        x-exportParamName: "Id"
      responses:
        200:
          description: "Successful Operation"
          schema:
            $ref: "#/definitions/Article"
        400:
          description: "Bad Request"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "Wrong ArticleId"
        404:
          description: "Not Found"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "Article Not Exists"
  /article/{id}/comments:
    get:
      tags:
      - "article"
      summary: "Get all comments of an article"
      description: "Get all comments of an article"
      operationId: "GetCommentsOfArticle"
      produces:
      - "application/json"
      parameters:
      - name: "id"
        in: "path"
        description: "The only id of the article to return"
        required: true
        type: "integer"
        x-exportParamName: "Id"
      responses:
        200:
          description: "Successful Operation"
          schema:
            $ref: "#/definitions/Comments"
        400:
          description: "Bad Request"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "Wrong ArticleId"
        404:
          description: "Not Found"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "Article Not Exists"
                - "Comment Not Exists"
  /auth/signup:
    post:
      tags:
      - "user"
      summary: "sign up"
      description: "Create a new user with the only username"
      operationId: "SignUp"
      produces:
      - "application/json"
      parameters:
      - in: "body"
        name: "body"
        description: "Created user object"
        required: true
        schema:
          $ref: "#/definitions/User"
        x-exportParamName: "Body"
      responses:
        200:
          description: "Successful Operation"
        400:
          description: "Bad Requested"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "User Exists"
                - "Wrong Username or Password"
                - "..."
  /auth/signin:
    post:
      tags:
      - "user"
      summary: "sign in"
      description: "Check user with username and password"
      operationId: "SignIn"
      produces:
      - "application/json"
      parameters:
      - in: "body"
        name: "body"
        required: true
        schema:          
          $ref: "#/definitions/User"
      responses:
        200:
          description: "Successful Operation"
          schema:
            type: object
            properties:
              token: 
                type: string
        404:
          description: "Not Found"
          schema:
            type: object
            properties:
              error: 
                type: string
                example: "Wrong Username or Password"

  /article/{id}/comment:
    post:
      tags:
      - "user"
      summary: "create comment"
      description: "user creates a comment for the article"
      operationId: "CreateComment"
      parameters:
      - name: "id"
        in: "path"
        required: true
        type: "integer"
        x-exportParamName: "Id"
      - in: "body"
        name: "body"
        required: true
        schema:
          type: object
          properties:
            content:
              type: string
            author:
              type: string
      responses:
        200:
          description: "Successful Operation"
          schema:
            $ref: "#/definitions/Comment"
        400:
          description: "Bad Request"
          schema:
            type: object
            properties:
              error: 
                type: string
                example:
                - "Article Not Exists"
                - "Wrong ArticleId"
                - "There is no content in your article"
                - "..."
securityDefinitions:
  petstore_auth:
    type: "oauth2"
    authorizationUrl: "http://petstore.swagger.io/oauth/dialog"
    flow: "implicit"
    scopes:
      write:pets: "modify pets in your account"
      read:pets: "read your pets"
  api_key:
    type: "apiKey"
    name: "api_key"
    in: "header"
definitions:
  User:
    type: "object"
    required:
    - "password"
    - "username"
    properties:
      username:
        type: "string"
      password:
        type: "string"
    example:
      password: "password"
      username: "username"
  Tag:
    type: "object"
    properties:
      name:
        type: "string"
    example:
      name: "name"
  Article:
    type: "object"
    required:
    - "author"
    - "content"
    - "id"
    - "name"
    properties:
      id:
        type: "integer"
      name:
        type: "string"
      tags:
        type: "array"
        items:
          $ref: "#/definitions/Tag"
      date:
        type: "string"
      content:
        type: "string"
    example:
      date: "date"
      author: "author"
      name: "name"
      id: 0
      content: "content"
      tags:
      - name: "name"
      - name: "name"
  Comment:
    type: "object"
    required:
    - "articleId"
    - "author"
    - "content"
    - "date"
    properties:
      date:
        type: "string"
      content:
        type: "string"
      author:
        type: "string"
      articleId:
        type: "integer"
    example:
      date: "date"
      author: "author"
      articleId: 0
      content: "content"
  Comments :
    properties:
      contents:
        type: "array"
        items:
          $ref: '#/definitions/Comment'
  ArticleResponse:
    properties:
      id:
        type: "integer"
      name:
        type: "string"
  ArticlesResponse:
    properties:
      Articles:
        type: "array"
        items:
          $ref: "#/definitions/ArticleResponse"
externalDocs:
  description: "Find out more about Swagger"
  url: "http://swagger.io"

点击在线编辑器上方的Generate Client,选择html2,会生成一个压缩包,里面包含了根据yaml生成的html页面。
在这里插入图片描述
点击上方的Generate Server,选择go-server,生成一个已配置好的代码包。
在这里插入图片描述
打开其中的routers.go,已经实现了路由匹配和匹配函数调用。
在这里插入图片描述
除此之外,article.go定义好了article结构体,article_api.go定义了匹配函数,这样只需要在包含匹配函数的文件里实现功能即可,比较方便。

在GitHub上有swagger-vue-client的生成swagger-vue
生成vue-api-client.js,按照API服务发送请求,获取response的回调函数。
在这里插入图片描述

后端

作业要求使用boltDB,boltdb的目标是为不需要完整数据库服务器(如Postgres或MySQL)的项目提供一个简单,快速,可靠的数据库。
BoltDB设计源于LMDB,具有使用Go语言编写、不需要服务器即可运行、支持数据结构、直接使用API存取数据,没有查询语句等特点
BoltDB是键值存储,这意味着没有像SQL RDBMS中的表,没有行,没有列。键值对存储在Buckets中,它们旨在对相似的对进行分组。因此,为了获得Value,需要知道该Value所在的桶和钥匙。

db, err := bolt.Open("my.db", 0600, nil)
    if err != nil {
        log.Fatal(err)
    }
	defer db.Close()

开启数据库。

err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("User"))
		if b != nil {
			v := b.Get([]byte(user.Username))
			if ByteSliceEqual(v, []byte(user.Password)) {
				return nil
			} else {
				return errors.New("Wrong Username or Password")
			}
		} else {
			return errors.New("Wrong Username or Password")
		}
	})

读入数据。

err = db.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte("User"))
		if err != nil {
			return err
		}
		return b.Put([]byte(user.Username), []byte(user.Password))
	})

写入数据。

在请求和响应中使用json表示body,服务端收到请求时需要反序列化成结构体,发送响应时需要序列化成字符串。

comment := &Comment{
	Date:  time.Now().Format("2006-01-02 15:04:05"),
	Content: "",
	Author: "",
	ArticleId: Id,
}
err = json.NewDecoder(r.Body).Decode(&comment)
...
json, err := json.Marshal(response)

mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

easy mock可以直接导入swagger的所有接口,还能适配注释。

在travis上登陆仓库
在这里插入图片描述
打勾需要测试的仓库。
在这里插入图片描述
开发好需要集成的library以及测试用例后,在根目录新建.travis.yml

language: go
go:
  - 1.x
  - '1.8'
  - '1.9'
  - 1.10.x
script:
  - go test -v ./...
env:
  global:
    secure: kr5JHNTYsh/jezvk88qP91arb+UD/op/5CyOFY7uNYpJ6ZSsJY5fDKyZHjf0VSFmaYqJFMPl6uCASE9baiepeGvBFcy8aI9CNsbLzj2uBNjqqYPmvYGnBjpzp8yknVJKRTitF/kkWtzZcWImHnpvNGHuzXxp/EIBeJtNwjcCRoP/qfGhlZKbLsYFvlWkmRYb0dr8RM5mlmGXPZi8q7m+soVRO8Zjr4QQccybgmhonxlcUrHr6ro+yjjQefoJXRufqoRX0sGyecGYucC4nUpWl5hkDPkQE+Mekhz+rF657SwNsn8nXOFnnUuwsPXE26ak5xF1roEcFk2CpwGZuT7smJZPtw1inXFdIaW+4qllbyxMJkylvFZa5IcvLT3+/eKaQc8Fg6PoxJH0PF3RdtoQVB31cQiPWNm1SecQ6wC64WA/5qN4T5OoRfpt60BFDAITdS62dQGu5LSepcXMWXhxCdQPeDm5Qce6wjJXURubJMpBm0mPWwCNZhJyRw1G5TTyO25NckXQRlObrjltvwAd+7OEUcsYXqhdPtUTIVy6w3XOwT2eC/hP0Yi7qqUMMlJTHUW7Lb9zsEc4UB5BVwgeZ5Y9bVbknJfpt3ygcXAJeeDYxwV9g16KoS7HMFPzwrqlHbiBytIahqarBd4enwqR5RYQPEyetiIDLaJA4SyQ0cE=
notifications:
  email:
    recipients:
      - gaozhen0516@gmail.com
    on_success: always

更改项目文件,push到github,travis会自动运行测试脚本。

终端执行travis encrypt AMAP_KEY="xxxx" --add,AMAP_KEY是环境变量名称,可以对敏感数据xxxx进行加密。
添加邮箱可以接收测试结果通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值