Go RESTful API 服务示例

Golang 投稿 59800 1 评论

Go RESTful API 服务示例

何时使用单体 RESTful 服务

商城单体 RESTful 服务

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

单体服务实现

在  目录下分别创建 , ,  和,其中  为聚合的  文件,通过  导入,文件列表如下:

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

Mall API 定义

的定义如下,其中  表示这是  的  语法

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)
}

生成单体服务

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

$ 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

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

  • :存放  描述文件

  • :用来定义项目配置,所有的配置项都可以写在  中

  • :服务的配置定义

  • : 文件中定义的路由对应的  的实现

  • :用来放每个路由对应的业务逻辑,之所以区分  和  是为了让业务处理部分尽可能减少依赖,把  和逻辑处理代码隔离开,便于后续拆分成

  • :用来定义业务逻辑处理的依赖,我们可以在  函数里面创建依赖的资源,然后通过  传递给  和

  • :定义了  请求和返回数据结构

  • : 函数所在文件,文件名和  定义中的  同名,去掉了后缀

$ go run mall.go
Starting server at 0.0.0.0:8888...
实现业务逻辑

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

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),
    }
}

在  中实现根据  查询所有订单的方法

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
}

在  的  方法中调用  方法

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
}

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

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
}

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

总结

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

项目地址

https://github.com/zeromicro/go-zero

star 支持我们!

微信交流群

微服务实践』公众号并点击 交流群 获取社区群二维码。

如果你有  的使用心得文章,或者源码学习笔记,欢迎通过公众号联系投稿!

编程笔记 » Go RESTful API 服务示例

赞同 (106) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(1)个小伙伴在吐槽
  1. 让读者有更多选择。
    忧郁的精灵 2023-09-11 06:30 (1年前) 回复