语法介绍

niuhe 语法主要包括注释,常量(枚举), 数据类型(class/struct), 接口定义(路由), 数据成员定义五个部分。 idl 语法大致与 python 相通, 插件自定义了 niuhe 后缀的语法高亮和自动补全,也可选择 python 来做语法高亮

应用名称

应用名称需定义在入口文件(all.niuhe)最开始,使用 #app=应用名 来定义。

#app=demo

注释

注释同 python 相同, 单行注释使用 #, 多行注释使用三个引号 ''', 类和常量的说明用注释的方式写在类定义的下一行。

常量(枚举)

class Language(ConstGroup): '''语言枚举类''' ZH = Item("zh", "中文") EN = Item("en", "英文") class LanguageType(ConstGroup): '''语言类型枚举''' ZH_CN = Item(1, "简体中文") ZH_TW = Item(2, "繁体中文")

常量定义以 class 开头, 继承 ConstGroup, 内部通过 Item 来定义具体的常量值。常量仅支持 StringInteger 类型,且必须指定一个唯一标识符和一个显示名称。上述定义生成后的代码(src/demo/app/common/consts/gen_consts.go)为

package consts // Generated by niuhe.idl import "github.com/ma-guo/niuhe" var Language struct { *niuhe.StringConstGroup ZH niuhe.StringConstItem `name:"中文" value:"zh"` // value: zh, name: 中文 EN niuhe.StringConstItem `name:"英文" value:"en"` // value: en, name: 英文 } // 语言类型枚举 var LanguageType struct { *niuhe.IntConstGroup ZH_CN niuhe.IntConstItem `name:"简体中文" value:"1"` // value: 1, name: 简体中文 ZH_TW niuhe.IntConstItem `name:"繁体中文" value:"2"` // value: 2, name: 繁体中文 } func init() { niuhe.InitConstGroup(&Language) niuhe.InitIntConstGroup(&LanguageType) }

数据类型(class/struct)

在上一节的 hello world 示例中,我们定义了一个简单的 接口的入参和出参类

class HelloReq(): '''测试请求''' name = required.String(desc='用户名') class HelloRsp(Message): '''测试响应''' greeting = required.String(desc='问候语')

数据类型定义以 class 开头, 继承 Message, Message 可不写, 类后跟随以注释形式的类说明(可选)和成员定义。 当类无成员时以 pass 做标记即可,可如下:

class HelloReq(): '''测试请求, 无参数''' pass

上述定义生成的 struct 代码为:

package protos // Generated by niuhe.idl // 此文件由 niuhe.idl 自动生成, 请勿手动修改 // 测试请求 type HelloReq struct { Name string `json:"name" zpf_name:"name" zpf_reqd:"true"` // 用户名 } // 测试响应 type HelloRsp struct { Greeting string `json:"greeting" zpf_name:"greeting" zpf_reqd:"true"` // 问候语 }

类的继承

类继承自Message可以继承自自定义的其他类, 通过在类名后添加括号的方式实现:

class NihaoReq(HelloReq): '''你好请求''' mingzi = required.String(desc='名字')

生成的 struct 代码为:

// 你好请求 type NihaoReq struct { Name string `json:"name" zpf_name:"name" zpf_reqd:"true"` // 用户名 Mingzi string `json:"mingzi" zpf_name:"mingzi" zpf_reqd:"true"` // 名字 }

成员定义

成员定于语法为:

member_name = label.type(desc='', cls=..., group=...)

其中

  • label 可以是 required(必填的), optional(可选的), repeated(重复的/数组),
  • type 可以是 Integer, Decimal, Float, Long, String, Boolean, Message, Enum,StringEnum, File, Any 11种数据类型。
  • desc: 描述信息, 可选, 建议填写成员字段说明, 否则失去文档定义语言意义。
  • group: 指定 enumgroup, 仅当 typeEnumStringEnum 时有效和必填, 如 lang=required.Enum(desc='语言枚举', houp=LanguageType)
  • cls: 指定类的类型,仅当 typeEnumStringEnum 时有效和必填,如 message=optional.Message(desc='消息体',cls=HelloReq) 这里 requiredoptional 仅对入参有意义, 出参在 go 中无此限制。 repeated 在入参中同optional

接口定义(路由)

接口定义以 with services(): 为开始标记, 后面跟随一个或多个路由定义,每个路由定义如下:

http_method('接口说明', 'api_path(/mode/view/method/)', 入参类名(可选), 出参类名(可选))

其中

  • http_method 可以是 GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS 等 http method
  • 接口说明: 接口的简要描述, 如 '获取用户信息'
  • api_path: 必须是三段式 api 的路径, 如 /api/hello/world/
  • 入参类名: 可选, 如 HelloReq 当不填写时,得自己处理入参和出参的解析, 入参和出参得同时可选或同时必填。
  • 出参类名: 可选, 如 HelloRsp 当不填写时,得自己处理入参和出参的解析, 入参和出参得同时可选或同时必填。 本章节第二小节定义的路由如下:
with services(): GET('示例接口', '/api/hello/world/', NihaoReq, HelloRsp)

生成的代码在 src/demo/app/api/views/[gen_]hello_views.go 文件中。

include 其他文件

在定义类时,可以通过 include 关键字引入其他文件中的类. 如在 comm.niuhe 文件中定义了如下内容:

# 公共类型定义 class NoneResp() '''空响应''' pass class NoneReq(): '''空请求''' pass

此时可在 all.niuhe 文件中通过 include 引入:

include('comm.niuhe') // 然后通过 comm.NoneReq, comm.NoneResp 来使用这些类

其他说明

入参参数中, 并不支持解析复杂结构, 如 Message, Any, Dict 等. 而出参则无此限制, 可以自由定义返回的 json 数据格式。