新项目开发决定使用proto来定义api,服务同时提供grpc/http接口
然后通过proto生成swagger文件,导入到yapi中实现自动接口维护
由于之前项目未应用,于是写了个demo以作内部演示用,主要是介绍http rest接口的定义与请求的传参和 message 定义之间的关联
目录结构
1 | ├── gotest |
proto文件
api.proto
api的定义,参考 http.proto
为直观演示,放飞自我定义的接口,勿喷
1 | syntax = "proto3"; |
代码生成
annotations.proto 等依赖的 proto 文件如果没有放在同一文件夹,需要使用 -I 指定其路径,注意import是否已包含了部分路径
1 | # 同时生成 pb,grpc,gateway 文件 |
demo
main.go
1 | package main |
验证
看代码,在接口处理方法中,打印了请求参数(调用String方法,零值将被忽略不打印),来演示入参的获取情况
结合使用了 json_name
,不了解的可以参考上一篇 在go的protobuf中进行自定义json tag标记及使用go run main.go
GET
1 | # 除路径外无任何传参 |
POST
1 | # body:"*" 但不传body,尝试 query string 传参 |
其它骚操作
1 | # body:"baz" 传递了json对象 |
总结
通过以上案例,结合proto定义,可直观的掌握如何应用grpc-gateway来同时提供http/grpc接口(grpc未测试)
简单总结如下:
- method为get时,path及queryString中的参数都会被映射到 reques 对应的 message 字段
- 传参字段名可以使用json_name指定名称,但不能与原字段名同时使用
- method为post时,会忽略queryString中的传参,只接收 path 及 body 中的传参
- option中省略了 body,则表示没有http请求体,参数只能通过 path 及 query string 传递,body 被忽略
- option中指定body为 * 时,参数只能通过 path 及 body 传递,query string 被忽略
- 所以如果使用 * ,要注意不能再通过 query string 传参了
- option中指定body指定为指定字段时,path/query string/body中的值都可以被接收
- json body中字段名可以使用json_name指定名称,但不能与原字段名同时使用
当然,也可以用用一个端口来监听,但需要根据http协议版本来区分是哪一类请求
1 | if r.ProtoMajor == 2 && strings.HasPrefix(r.Header.Get("Content-Type"), "application/grpc") { |
挑战
自定义响应格式
大多数公司会定义rest api时,指定一堆错误码,并随响应返回,通常类似
1 | { |
但直接使用grpc-gateway面临的挑战就是它直接返回了消息体,而未提供响应的hook来自定义响应体,错误时有方法,但正确响应时没有
同时开发者建议在response中定义相关的字段,是一个closed的issue #1610,目前没有其它好的方案直接使用
但是,如果这个接口不是直接对公网终端用户提供服务,前方有网关,或其它应用层处理,那么就可以在上层进行数据格式组织,即只对错误的情况预处理后返回,正确的留给上层包装
哪怕是通过runtime.WithForwardResponseOptionheader
中将错误写入自定义 header这种骚操作呢,总是有办法处理的
比如 自定义错误
1 | type httpError struct { |
其它的自定义函数可参考 Customizing your gateway
跨域
grpc也是http,http/2,所以可以在拦截器(interceptors)中进行跨域设置