自定义Service
本章将基于“快速创建HAS API服务”引导你创建一个自己的API服务,且可以通过http访问到
创建Service项目结构
在项目目录下执行命令,创建用于存放工程的文件夹
mkdir -p models/services
创建一个目录用于存放service代码,并在其目录下创建service.go、conf.go、slot.go
cd models/services
mkdir testsvs
cd testsvs
touch service.go conf.go slot.go
完成以上操作后,你的项目结构应该看起来像是这样:
.
├── api.json
├── conf.toml
├── go.mod
├── go.sum
├── main.go
└── models
└── services
└── testsvs
├── conf.go
├── service.go
└── slot.go
我们建议Service按照xxxsvs的命名规则来命名
编写Service代码
在对应的文件中写入代码
service.goservice.go文件主要用于处理和service相关的操作,例如在此service被注册时操作数据库初始化相关数据或service被注册时发起一个http请求等...
package testsvs
import (
"github.com/drharryhe/has/common/herrors"
"github.com/drharryhe/has/common/htypes"
"github.com/drharryhe/has/core"
)
type Service struct {
core.Service
conf TestService
TestName string // testName,你可以自定义这些字段,用于存储必要信息
}
// 我们建议在项目开发中,开发者为此处自定义的字段都写上注释,便于后期维护
func (this *Service) Open(s core.IServer, instance core.IService, args htypes.Any) *herrors.Error {
if err := this.Service.Open(s, instance, args); err != nil {
return err
}
this.TestName = ""
// 使用配置文件中的值
if this.conf.TestBool {
this.TestName = this.conf.TestName
}
return nil
}
func (this *Service) EntityStub() *core.EntityStub {
return core.NewEntityStub(&core.EntityStubOptions{Owner: this})
}
func (this *Service) Config() core.IEntityConf {
return &this.conf
}
conf.goconf.go主要用于描述配置文件中服务对应的结构体,它需要继承core.ServiceConf
package testsvs
import "github.com/drharryhe/has/core"
type TestService struct {
core.ServiceConf // 注意此处继承core.ServiceConf
}
slot.goslot.go用于处理接口的业务逻辑
package testsvs
import (
"github.com/drharryhe/has/common/herrors"
"github.com/drharryhe/has/common/hlogger"
"github.com/drharryhe/has/core"
)
type TestRequest struct {
core.SlotRequestBase // 注意继承 core.SlotRequestBase
// 注意类型要带指针,才能使用data验证
Name *string `json:"name" param:"require"` // 名称
Change *bool `json:"change" param:"require"` // 是否改变name param:"require"代表此字段必传
}
// 我们建议在项目开发中,开发者为此处的每个字段都写上注释,便于后期维护
func (this *Service) TestSlot(req *TestRequest, res *core.SlotResponse) {
hlogger.Info("request name:", *req.Name) // HAS中带有log组件可以直接使用
hlogger.Info("request change:", *req.Change)
resultName := *req.Name
if *req.Change {
resultName = this.TestName
}
if resultName == "" {
// 定义error debug模式下 New里面的内容会直接以log输出 所以不需要单独打log
res.Error = herrors.ErrSysInternal.New("名称为空").D("描述错误")
return
}
// 返回数据
this.Response(res, map[string]interface{}{
"change": *req.Change,
"resultName": resultName,
}, nil)
}
修改main.go、conf.toml和api.json
main.go
func main() {
// 创建gateway
gateway := core.NewAPIGateway(&core.APIGatewayOptions{
// Server配置
ServerOptions: core.ServerOptions{
// 路由配置
Router: hlocalrouter.New(),
},
Connectors: []core.IAPIConnector{
hwebconnector.New(), // Web服务
},
Packers: []core.IAPIDataPacker{
hjsonpacker.New(),
},
})
// 注册服务
gateway.Server().RegisterService(&hellosvs.Service{}, nil)
+++ gateway.Server().RegisterService(&testsvs.Service{}, nil) // 添加testsvs
// 启动服务
gateway.Start()
}
conf.toml添加以下内容
[TestService]
Name = 'test' // Name是必须的
TestBool = true
TestName = 'HAS'
api.json
{
"name": "HAS api",
"versions": [
{
"version": "v1",
"apis": [
{
"name": "hello",
"desc": "hello",
"disabled": false,
"endpoint": {
"service": "hello",
"slot": "HelloSlot"
}
},
// ==== 新增 ====
+++ {
+++ "name": "test", // 请求接口名, http://127.0.0.1:8989/v1/test
+++ "desc": "test", // 接口描述
+++ "disabled": false, // 屏蔽接口
+++ "endpoint": {
+++ "service": "test", // 对应配置文件中的Service的name
+++ "slot": "TestSlot" // 对应slot.go中的方法名
+++ }
+++ }
// === 新增 ===
]
}
]
}
运行
go run main.go
注
以下访问使用POST方法 body, Content-Type: application/json
如果使用GET方法访问,bool值会被解析为string类型,如果开发过程中遇到,需要自行转换
访问http://127.0.0.1:8989/v1/test,参数如下:
{
"name": "张三",
"change":false
}
我们会得到结果
{
"data": {
"resultName": "张三",
"change": false
},
"error": {
"code": 0,
"desc": "",
"cause": ""
}
}
如果参数如下
{
"name": "张三",
"change": true
}
会得到返回结果如下
{
"data": {
"change": true,
"resultName": "HAS"
},
"error": {
"code": 0,
"desc": "",
"cause": ""
}
}
可以看到resultName为配置文件中的TestName了
如果参数如下:
{
"name": "",
"change": false
}
会得到返回结果如下,并在log中显示error调用信息
{
"data": {},
"error": {
"code": 101,
"desc": "此处信息会返回前端",
"fingerprint": "56d1aa7de1e8f373a137e2623180271e",
"cause": "名称为空"
}
}
提示
HAS有一套很好的错误处理方案
在debug模式下会打一个error的log和详细的调用信息,便于我们在开发中快速定位问题
此处的fingerprint只有在Debug模式下才会返回,主要用于在一些无法进入服务器查看log的情况下定位问题
服务之间的调用
当我们在某个service内需要调用另一个service的接口时,我们可以使用下面这种方法来调用
在testsvs的Open方法中插入以下代码,然后重启,并查看log
如上述代码所示,通过this.Server().RequestService('serviceName', 'slotName', params)便可以访问到对应的slot