Go快速开发一个RESTful API服务

目录
  • 何时使用单体 RESTful 服务
  • 商城单体 RESTful 服务
  • 单体服务实现
    • Mall API 定义
    • 账号模块 API 定义
    • 订单模块 API 定义
    • 商品模块 API 定义
    • 生成单体服务
    • 实现业务逻辑
  • 总结

何时使用单体 RESTful 服务

对于很多初创公司来说,业务的早期我们更应该关注于业务价值的交付,而单体服务具有架构简单,部署简单,开发成本低等优点,可以帮助我们快速实现产品需求。我们在使用单体服务快速交付业务价值的同时,也需要为业务的发展预留可能性,所以我们一般会在单体服务中清晰的拆分不同的业务模块。

商城单体 RESTful 服务

我们以商城为例来构建单体服务,商城服务一般来说相对复杂,会由多个模块组成,比较重要的模块包括账号模块、商品模块和订单模块等,每个模块会有自己独立的业务逻辑,同时每个模块间也会相互依赖,比如订单模块和商品模块都会依赖账号模块,在单体应用中这种依赖关系一般是通过模块间方法调用来完成。一般单体服务会共享存储资源,比如 MySQL 和 Redis 等。

单体服务的整体架构比较简单,这也是单体服务的优点,客户请求通过 DNS 解析后通过 Nginx 转发到商城的后端服务,商城服务部署在 ECS 云主机上,为了实现更大的吞吐和高可用一般会部署多个副本,这样一个简单的平民架构如果优化好的话也是可以承载较高的吞吐的。

商城服务内部多个模块间存在依赖关系,比如请求订单详情接口 /order/detail,通过路由转发到订单模块,订单模块会依赖账号模块和商品模块组成完整的订单详情内容返回给用户,在单体服务中多个模块一般会共享数据库和缓存。

单体服务实现

接下来介绍如何基于 go-zero 来快速实现商城单体服务。使用过 go-zero 的同学都知道,我们提供了一个 API 格式的文件来描述 Restful API,然后可以通过 goctl 一键生成对应的代码,我们只需要在 logic 文件里填写对应的业务逻辑即可。商城服务包含多个模块,为了模块间相互独立,所以不同模块由单独的 API 定义,但是所有的 API 的定义都是在同一个 service (mall-api) 下。

在 api 目录下分别创建 user.api, order.api, product.api 和 mall.api,其中 mall.api 为聚合的 api 文件,通过 import 导入,文件列表如下:

api
|-- mall.api
|-- order.api
|-- product.api
|-- user.api

Mall API 定义

mall.api 的定义如下,其中 syntax = “v1” 表示这是 zero-api 的 v1 语法

syntax = "v1"
import "user.api"
import "order.api"
import "product.api"

账号模块 API 定义

  • 查看用户详情
  • 获取用户所有订单
syntax = "v1"
type (
    UserRequest {
        ID int64 `path:"id"`
    }
    UserReply {
        ID      int64   `json:"id"`
        Name    string  `json:"name"`
        Balance float64 `json:"balance"`
    }
    UserOrdersRequest {
        ID int64 `path:"id"`
    }
    UserOrdersReply {
        ID       string `json:"id"`
        State    uint32 `json:"state"`
        CreateAt string `json:"create_at"`
    }
)
service mall-api {
    @handler UserHandler
    get /user/:id (UserRequest) returns (UserReply)
    @handler UserOrdersHandler
    get /user/:id/orders (UserOrdersRequest) returns (UserOrdersReply)
}

订单模块 API 定义

  • 获取订单详情
  • 生成订单
syntax = "v1"
type (
    OrderRequest {
        ID string `path:"id"`
    }
    OrderReply {
        ID       string `json:"id"`
        State    uint32 `json:"state"`
        CreateAt string `json:"create_at"`
    }
    OrderCreateRequest {
        ProductID int64 `json:"product_id"`
    }
    OrderCreateReply {
        Code int `json:"code"`
    }
)
service mall-api {
    @handler OrderHandler
    get /order/:id (OrderRequest) returns (OrderReply)
    @handler OrderCreateHandler
    post /order/create (OrderCreateRequest) returns (OrderCreateReply)
}

商品模块 API 定义

  • 查看商品详情
syntax = "v1"
type ProductRequest {
    ID int64 `path:"id"`
}
type ProductReply {
    ID    int64   `json:"id"`
    Name  string  `json:"name"`
    Price float64 `json:"price"`
    Count int64   `json:"count"`
}
service mall-api {
    @handler ProductHandler
    get /product/:id (ProductRequest) returns (ProductReply)
}

生成单体服务

已经定义好了 API,接下来用 API 生成服务就会变得非常简单,我们使用 goctl 生成单体服务代码。

$ goctl api go -api api/mall.api -dir .

生成的代码结构如下:

.
├── api
│   ├── mall.api
│   ├── order.api
│   ├── product.api
│   └── user.api
├── etc
│   └── mall-api.yaml
├── internal
│   ├── config
│   │   └── config.go
│   ├── handler
│   │   ├── ordercreatehandler.go
│   │   ├── orderhandler.go
│   │   ├── producthandler.go
│   │   ├── routes.go
│   │   ├── userhandler.go
│   │   └── userordershandler.go
│   ├── logic
│   │   ├── ordercreatelogic.go
│   │   ├── orderlogic.go
│   │   ├── productlogic.go
│   │   ├── userlogic.go
│   │   └── userorderslogic.go
│   ├── svc
│   │   └── servicecontext.go
│   └── types
│       └── types.go
└── mall.go

解释一下生成的代码结构:

  • api:存放 API 描述文件
  • etc:用来定义项目配置,所有的配置项都可以写在 mall-api.yaml 中
  • internal/config:服务的配置定义
  • internal/handler:API 文件中定义的路由对应的 handler 的实现
  • internal/logic:用来放每个路由对应的业务逻辑,之所以区分 handler 和 logic 是为了让业务处理部分尽可能减少依赖,把 HTTP requests 和逻辑处理代码隔离开,便于后续拆分成 RPC service
  • internal/svc:用来定义业务逻辑处理的依赖,我们可以在 main 函数里面创建依赖的资源,然后通过 ServiceContext 传递给 handler 和 logic
  • internal/types:定义了 API 请求和返回数据结构
  • mall.go:main 函数所在文件,文件名和 API 定义中的 service 同名,去掉了后缀 -api

生成的服务不需要做任何修改就可以运行:

$ go run mall.go
Starting server at 0.0.0.0:8888...

实现业务逻辑

接下来我们来一起实现一下业务逻辑,出于演示目的逻辑会比较简单,并非真正业务逻辑。

首先,我们先来实现用户获取所有订单的逻辑,因为在用户模块并没有订单相关的信息,所以我们需要依赖订单模块查询用户的订单,所以我们在 UserOrdersLogic 中添加对 OrderLogic 依赖

type UserOrdersLogic struct {
    logx.Logger
    ctx        context.Context
    svcCtx     *svc.ServiceContext
    orderLogic *OrderLogic
}
func NewUserOrdersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserOrdersLogic {
    return &UserOrdersLogic{
        Logger:     logx.WithContext(ctx),
        ctx:        ctx,
        svcCtx:     svcCtx,
        orderLogic: NewOrderLogic(ctx, svcCtx),
    }
}

在 OrderLogic 中实现根据 用户id 查询所有订单的方法

func (l *OrderLogic) ordersByUser(uid int64) ([]*types.OrderReply, error) {
    if uid == 123 {
        // It should actually be queried from database or cache
        return []*types.OrderReply{
            {
                ID:       "236802838635",
                State:    1,
                CreateAt: "2022-5-12 22:59:59",
            },
            {
                ID:       "236802838636",
                State:    1,
                CreateAt: "2022-5-10 20:59:59",
            },
        }, nil
    }
    return nil, nil
}

在 UserOrdersLogic 的 UserOrders 方法中调用 ordersByUser 方法

func (l *UserOrdersLogic) UserOrders(req *types.UserOrdersRequest) (*types.UserOrdersReply, error) {
    orders, err := l.orderLogic.ordersByUser(req.ID)
    if err != nil {
        return nil, err
    }
    return &types.UserOrdersReply{
        Orders: orders,
    }, nil
}

这时候我们重新启动 mall-api 服务,在浏览器中请求获取用户所有订单接口

http://localhost:8888/user/123/orders

返回结果如下,符合我们的预期

{
    "orders": [
        {
            "id": "236802838635",
            "state": 1,
            "create_at": "2022-5-12 22:59:59"
        },
        {
            "id": "236802838636",
            "state": 1,
            "create_at": "2022-5-10 20:59:59"
        }
    ]
}

接下来我们再来实现创建订单的逻辑,创建订单首先需要查看该商品的库存是否足够,所以在订单模块中需要依赖商品模块。

type OrderCreateLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
    productLogic *ProductLogic
}
func NewOrderCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *OrderCreateLogic {
    return &OrderCreateLogic{
        Logger:       logx.WithContext(ctx),
        ctx:          ctx,
        svcCtx:       svcCtx,
        productLogic: NewProductLogic(ctx, svcCtx),
    }
}

创建订单的逻辑如下

const (
    success = 0
    failure = -1
)
func (l *OrderCreateLogic) OrderCreate(req *types.OrderCreateRequest) (*types.OrderCreateReply, error) {
    product, err := l.productLogic.productByID(req.ProductID)
    if err != nil {
        return nil, err
    }
    if product.Count > 0 {
        return &types.OrderCreateReply{Code: success}, nil
    }
    return &types.OrderCreateReply{Code: failure}, nil
}

依赖的商品模块逻辑如下

func (l *ProductLogic) Product(req *types.ProductRequest) (*types.ProductReply, error) {
    return l.productByID(req.ID)
}
func (l *ProductLogic) productByID(id int64) (*types.ProductReply, error) {
    return &types.ProductReply{
        ID:    id,
        Name:  "apple watch 3",
        Price: 3333.33,
        Count: 99,
    }, nil
}

以上可以看出使用 go-zero 开发单体服务还是非常简单的,有助于我们快速开发上线,同时我们还做了模块的划分,为以后做微服务的拆分也打下了基础。

总结

通过以上的示例可以看出使用 go-zero 实现单体服务非常简单,只需要定义 api 文件,然后通过 goctl 工具就能自动生成项目代码,我们只需要在logic中填写业务逻辑即可,这里只是为了演示如何基于 go-zero 快速开发单体服务并没有涉及数据库和缓存的操作,其实我们的 goctl 也可以一键生成 CRUD 代码和 cache 代码,对于开发单体服务来说可以起到事半功倍的效果。

并且针对不同的业务场景,定制化的需求也可以通过自定义模板来实现,还可以在团队内通过远程 git 仓库共享自定义业务模板,可以很好的实现团队协同。

项目地址 github.com/zeromicro/g…

以上就是Go快速开发一个RESTful API服务的详细内容,更多关于Go开发RESTful API服务的资料请关注我们其它相关文章!

(0)

相关推荐

  • Django开发RESTful API实现增删改查(入门级)

    数据库中有user表如下: 新建一个Django项目: django-admin.py startproject myDjango<project_name> 目录介绍 myDjango/ ├── manage.py # 管理文件 └── myDjango # 项目目录 ├── __ init __.py ├── settings.py # 配置文件 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模

  • go 原生http web 服务跨域restful api的写法介绍

    错误写法 func main() { openHttpListen() } func openHttpListen() { http.HandleFunc("/", receiveClientRequest) fmt.Println("go server start running...") err := http.ListenAndServe(":9090", nil) if err != nil { log.Fatal("Liste

  • 详解Go语言RESTful JSON API创建

    RESTful API在Web项目开发中广泛使用,本文针对Go语言如何一步步实现RESTful JSON API进行讲解, 另外也会涉及到RESTful设计方面的话题. 也许我们之前有使用过各种各样的API, 当我们遇到设计很糟糕的API的时候,简直感觉崩溃至极.希望通过本文之后,能对设计良好的RESTful API有一个初步认识. JSON API是什么? JSON之前,很多网站都通过XML进行数据交换.如果在使用过XML之后,再接触JSON, 毫无疑问,你会觉得世界多么美好.这里不深入JSO

  • Django JWT Token RestfulAPI用户认证详解

    一般情况下我们Django默认的用户系统是满足不了我们的需求的,那么我们会对他做一定的扩展 创建用户项目 python manage.py startapp users 添加项目apps settings.py INSTALLED_APPS = [ ... 'users.apps.UsersConfig', ] 添加AUTH_USRE_MODEL 替换默认的user AUTH_USER_MODEL = 'users.UserProfile' 如果说想用全局认证需要在配置文件中添加 # 全局认证f

  • 基于Go语言构建RESTful API服务

    目录 什么是 RESTful API 一个简单的 RESTful API RESTful JSON API Gin 框架 引入 Gin 框架 使用 Gin 框架 新增一个用户 获取特定的用户 总结 在实际开发项目中,你编写的服务可以被其他服务使用,这样就组成了微服务的架构:也可以被前端调用,这样就可以前后端分离.那么,本文主要介绍什么是 RESTful API,以及 Go 语言是如何玩转 RESTful API 的. 什么是 RESTful API RESTful API 是一套规范,它可以规范

  • Go快速开发一个RESTful API服务

    目录 何时使用单体 RESTful 服务 商城单体 RESTful 服务 单体服务实现 Mall API 定义 账号模块 API 定义 订单模块 API 定义 商品模块 API 定义 生成单体服务 实现业务逻辑 总结 何时使用单体 RESTful 服务 对于很多初创公司来说,业务的早期我们更应该关注于业务价值的交付,而单体服务具有架构简单,部署简单,开发成本低等优点,可以帮助我们快速实现产品需求.我们在使用单体服务快速交付业务价值的同时,也需要为业务的发展预留可能性,所以我们一般会在单体服务中清

  • 使用typescript快速开发一个cli的实现示例

    cli 的全称 command-line interface(命令行界面),也就是前端同学常用的脚手架,比如 yo.vue cli.react cli 等. cli 可以方便我们快速创建项目,下图是引用 vue cli 的介绍: 创建项目 运行下面的命令,创建一个项目: npm init 执行命令完成后,可以看到项目根目录只有一个 package.json 文件. 在 package.json 文件增加 bin 对象,并指定入口文件 dist/index.js. 在命令行运行需要在入口文件的第一

  • spring常用注解开发一个RESTful接口示例

    目录 一.开发REST接口 1.第一步:定义资源(对象) 2.第二步:HTTP方法与Controller(动作) 二.统一规范接口响应的数据格式 一.开发REST接口 在本专栏之前的章节中已经给大家介绍了 Spring常用注解及http数据转换教程 Spring Boot提高开发效率必备工具lombok使用 Spring Boot开发RESTful接口与http协议状态表述 本节内容就是将之前学到的内容以代码的方式体现出来. 1. 第一步:定义资源(对象) @Data @Builder publ

  • 快速开发一个PHP扩展图文教程

    需求:比如开发一个叫做 heiyeluren 的扩展,扩展里就一个函数 heiyeluren_test(),输入一个字符串,函数返回:Your input string: xxxxx. 要求:了解C/C++编程,熟悉PHP编程 环境:下载一份php对应版本的源码,我这里是 php-5.2.6,先正常安装php,假设我们的php安装在 /usr/local/php 目录,源码在 /root/soft/php/php-5.2.6/,现在开始!步骤一:生成扩展框架 cd /root/soft/php/

  • golang 实现一个restful微服务的操作

    如何用net/http构建一个简单的web服务 Golang提供了简洁的方法来构建web服务 package main import ( "net/http" ) func HelloResponse(rw http.ResponseWriter, request *http.Request) { fmt.Fprintf(w, "Hello world.") } func main() { http.HandleFunc("/", HelloRe

  • 用Go+Vue.js快速搭建一个Web应用(初级demo)

    Vue.js做为目前前端最热门的库之一,为快速构建并开发前端项目多了一种思维模式.本文给大家介绍用Go+Vue.js快速搭建一个Web应用(初级demo). 环境准备: 1. 安装go语言,配置go开发环境: 2. 安装node.js以及npm环境: Gin的使用: 为了快速搭建后端应用,采用了Gin作为Web框架.Gin是用Golang实现的一种Web框架,api非常友好,且拥有出色的路由性能和详细的错误提示,如果你想快速开发一个高性能的生产环境,Gin是一个不错的选择. 下载和安装Gin:

  • python Flask实现restful api service

    一直在用node.js做后端,要逐步涉猎大数据范围,注定绕不过python,因此决定把一些成熟的东西用python来重写,一是开拓思路.通过比较来深入学习python:二是有目标,有动力,希望能持之以恒的坚持下去. 项目介绍 用python语言来写一个restful api service,数据库使用mysql.因为只做后端微服务,并且ORM的实现方式,采用自动生成SQL的方式来完成,因此选择了轻量级的flask作为web框架.如此选择,主要目的是针对中小规模的网络应用,能充分利用关系数据库的种

  • 详解SpringBoot restful api的单元测试

    现在我们来利用Spring Boot来构建一个RestFul API,具体如下: 1.添加Springboot测试注解 @RunWith(SpringRunner.class) @SpringBootTest public class UserControllerTest { } 2.伪造mvc环境 // 注入Spring 工厂 @Autowired private WebApplicationContext wac; //伪造mvc环境 private MockMvc mockMvc; @Be

  • 实现一个完整的Node.js RESTful API的示例

    前言 这篇文章算是对Building APIs with Node.js这本书的一个总结.用Node.js写接口对我来说是很有用的,比如在项目初始阶段,可以快速的模拟网络请求.正因为它用js写的,跟iOS直接的联系也比其他语言写的后台更加接近. 这本书写的极好,作者编码的思路极其清晰,整本书虽说是用英文写的,但很容易读懂.同时,它完整的构建了RESTful API的一整套逻辑. 我更加喜欢写一些函数响应式的程序,把函数当做数据或参数进行传递对我有着莫大的吸引力. 从程序的搭建,到设计错误捕获机制

随机推荐