刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • rpc

  • grpc

    • 【grpc】1.初识
    • 【grpc】2.上下文 metadata
    • 【grpc】3.健康检查
    • 【grpc】 4.keepalive
    • 【grpc】 5.命名解析
    • 【grpc】 6.中间件&拦截器
    • 【grpc】 7.负载均衡
    • 【grpc】 8.身份认证
    • 【grpc】 9.超时重试
    • 【grpc】 10.链路追踪
    • 【grpc】11.grpc-gw将gRPC 转 RESTful api
    • 【grpc】12.grpc-gw自定义选项
      • 消息序列化
        • 1. Custom serializer 自定义序列化器
        • 2. 在 JSON 中使用原型名称
        • 3. 根据Content-Type 自定义反序列化器
      • http请求头和gRPC client metadata映射
      • gRPC server metadata到http响应头 映射
      • 改变响应消息或设置响应标头
        • 1. 设置 HTTP headers
        • 2. 控制 HTTP 响应状态代码
        • 3. 修改默认响应body和error
        • 3.1 方案一
        • 3.2 方案二
      • 自定义错误返回
        • 1. 定义error response 格式
        • 2. 自定义路由错误处理
      • 获取 HTTP Path pattern
  • protobuf

  • rpc+grpc
  • grpc
bigox
2023-05-03
目录

【grpc】12.grpc-gw自定义选项

# 消息序列化

# 1. Custom serializer 自定义序列化器

  • 有时候可能希望在 MessagePack 而不是 JSON 中序列化请求/响应消息

    var m your.MsgPackMarshaler
    mux := runtime.NewServeMux(
    	runtime.WithMarshalerOption("application/x-msgpack", m),
    )
    
    1
    2
    3
    4
  • 默认配置参考

    runtime v2

    runtime.WithMarshalerOption(
      runtime.MIMEWildcard,
      &runtime.JSONPb{
        MarshalOptions: protojson.MarshalOptions{
          EmitUnpopulated: true,
          UseEnumNumbers:  true,
        },
      },
    ),
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    • runtime.JSONPb

      // JSONPb is a Marshaler which marshals/unmarshals into/from JSON
      // with the "google.golang.org/protobuf/encoding/protojson" marshaler.
      // It supports the full functionality of protobuf unlike JSONBuiltin.
      //
      // The NewDecoder method returns a DecoderWrapper, so the underlying
      // *json.Decoder methods can be used.
      type JSONPb struct {
      	protojson.MarshalOptions
      	protojson.UnmarshalOptions
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
    • protojson.MarshalOptions

      // Multiline specifies whether the marshaler should format the output in
      	// indented-form with every textual element on a new line.
      	// If Indent is an empty string, then an arbitrary indent is chosen.
      // 指定了marshaler是否应该以缩进的形式来格式化输出,每个文本元素都在一个新的行上。如果缩进是一个空字符串,那么将选择一个任意的缩进。
      	Multiline bool
      
      	// Indent specifies the set of indentation characters to use in a multiline
      	// formatted output such that every entry is preceded by Indent and
      	// terminated by a newline. If non-empty, then Multiline is treated as true.
      	// Indent can only be composed of space or tab characters.
      //指定了在多行格式化输出中使用的缩进字符集,这样每个条目都以缩进为前导,以换行为结束。如果非空,则多行被视为真。缩进只能由空格或制表符组成。
      	Indent string
      
      	// AllowPartial allows messages that have missing required fields to marshal
      	// without returning an error. If AllowPartial is false (the default),
      	// Marshal will return error if there are any missing required fields.
      // 允许在不返回错误的情况下对缺少必填字段的邮件进行处理。如果AllowPartial为false(默认),如果有任何缺失的必填字段,Marshal将返回错误。
      	AllowPartial bool
      
      	// UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
      	// field names.
      // 在JSON字段名中使用proto字段名而不是lowerCamelCase名称。
      	UseProtoNames bool
      
      	// UseEnumNumbers emits enum values as numbers.
      // 将枚举值作为数字返回
      	UseEnumNumbers bool
      
      	// EmitUnpopulated specifies whether to emit unpopulated fields. It does not
      	// emit unpopulated oneof fields or unpopulated extension fields.
      	// The JSON value emitted for unpopulated fields are as follows:
      // 指定是否返回未填充的字段。它不返回未填充的oneof字段或未填充的扩展字段。未填充字段的JSON值如下:
      	//  ╔═══════╤════════════════════════════╗
      	//  ║ JSON  │ Protobuf field             ║
      	//  ╠═══════╪════════════════════════════╣
      	//  ║ false │ proto3 boolean fields      ║
      	//  ║ 0     │ proto3 numeric fields      ║
      	//  ║ ""    │ proto3 string/bytes fields ║
      	//  ║ null  │ proto2 scalar fields       ║
      	//  ║ null  │ message fields             ║
      	//  ║ []    │ list fields                ║
      	//  ║ {}    │ map fields                 ║
      	//  ╚═══════╧════════════════════════════╝
      	EmitUnpopulated bool
      
      	// Resolver is used for looking up types when expanding google.protobuf.Any
      	// messages. If nil, this defaults to using protoregistry.GlobalTypes.
      	Resolver interface {
      		protoregistry.ExtensionTypeResolver
      		protoregistry.MessageTypeResolver
      	}
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
    • protojson.UnmarshalOptions

      // If AllowPartial is set, input for messages that will result in missing
      	// required fields will not return an error.
      // 如果设置了AllowPartial,对信息的输入将导致缺少必要的字段,将不会返回错误。
      	AllowPartial bool
      
      	// If DiscardUnknown is set, unknown fields are ignored.
      // 如果设置了DiscardUnknown,未知字段将被忽略。
      	DiscardUnknown bool
      
      	// Resolver is used for looking up types when unmarshaling
      	// google.protobuf.Any messages or extension fields.
      	// If nil, this defaults to using protoregistry.GlobalTypes.
      	Resolver interface {
      		protoregistry.MessageTypeResolver
      		protoregistry.ExtensionTypeResolver
      	}
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16

# 2. 在 JSON 中使用原型名称

  • protocol buffer 编译器会生成默认使用的camelCase JSON标签。如果你想使用proto文件中使用的确切大小写,设置 UseProtoNames: true :

    mux := runtime.NewServeMux(
    	runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
    		MarshalOptions: protojson.MarshalOptions{
    			UseProtoNames: true,
    		},
    		UnmarshalOptions: protojson.UnmarshalOptions{
    			DiscardUnknown: true,
    		},
    	}),
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 3. 根据Content-Type 自定义反序列化器

mux := runtime.NewServeMux(
	runtime.WithMarshalerOption("application/json+strict", &runtime.JSONPb{
		UnmarshalOptions: &protojson.UnmarshalOptions{
			DiscardUnknown: false, // explicit "false", &protojson.UnmarshalOptions{} would have the same effect
		},
	}),
)
1
2
3
4
5
6
7

# http请求头和gRPC client metadata映射

  • 如果你不喜欢默认的映射规则,或者你想传输全部的请求头,grpc-gw支持自定义映射规则

    • https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime#DefaultHeaderMatcher

      // DefaultHeaderMatcher is used to pass http request headers to/from gRPC context. This adds permanent HTTP header
      // keys (as specified by the IANA) to gRPC context with grpcgateway- prefix. HTTP headers that start with
      // 'Grpc-Metadata-' are mapped to gRPC metadata after removing prefix 'Grpc-Metadata-'.
      func DefaultHeaderMatcher(key string) (string, bool) {
      	key = textproto.CanonicalMIMEHeaderKey(key)
      	if isPermanentHTTPHeader(key) {
      		return MetadataPrefix + key, true
      	} else if strings.HasPrefix(key, MetadataHeaderPrefix) {
      		return key[len(MetadataHeaderPrefix):], true
      	}
      	return "", false
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
  1. 自定义HeaderMatcherFunc

  2. 使用WithIncomingHeaderMatcher注册映射函数

    func CustomMatcher(key string) (string, bool) {
    	switch key {
    	case "X-Custom-Header1":
    		return key, true
    	case "X-Custom-Header2":
    		return "custom-header2", true
    	default:
    		return key, false
    	}
    }
    
    mux := runtime.NewServeMux(
    	runtime.WithIncomingHeaderMatcher(CustomMatcher),
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  3. 要保留默认映射规则和您自己的规则, 可以这么写

    func CustomMatcher(key string) (string, bool) {
    	switch key {
    	case "X-User-Id":
    		return key, true
    	default:
    		return runtime.DefaultHeaderMatcher(key)
    	}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

# gRPC server metadata到http响应头 映射

  • grpc-gw默认:https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime#WithOutgoingHeaderMatcher

    // WithOutgoingHeaderMatcher returns a ServeMuxOption representing a headerMatcher for outgoing response from gateway.
    //
    // This matcher will be called with each header in response header metadata. If matcher returns true, that header will be
    // passed to http response returned from gateway. To transform the header before passing to response,
    // matcher should return modified header.
    func WithOutgoingHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption {
    	return func(mux *ServeMux) {
    		mux.outgoingHeaderMatcher = fn
    	}
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • 自定义

    if appendCustomHeader {
    	grpc.SendHeader(ctx, metadata.New(map[string]string{
    		"x-custom-header1": "value",
    	}))
    }
    
    1
    2
    3
    4
    5

# 改变响应消息或设置响应标头

# 1. 设置 HTTP headers

  • demo

    func myFilter(ctx context.Context, w http.ResponseWriter, resp proto.Message) error {
    	t, ok := resp.(*externalpb.Tokenizer)
    	if ok {
    		w.Header().Set("X-My-Tracking-Token", t.Token)
    		t.Token = ""
    	}
    	return nil
    }
    mux := runtime.NewServeMux(
    	runtime.WithForwardResponseOption(myFilter),
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 2. 控制 HTTP 响应状态代码

  • demo

    _ = grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "401"))
    ...
    func httpResponseModifier(ctx context.Context, w http.ResponseWriter, p proto.Message) error {
    	md, ok := runtime.ServerMetadataFromContext(ctx)
    	if !ok {
    		return nil
    	}
    
    	// set http status code
    	if vals := md.HeaderMD.Get("x-http-code"); len(vals) > 0 {
    		code, err := strconv.Atoi(vals[0])
    		if err != nil {
    			return err
    		}
    		// delete the headers to not expose any grpc-metadata in http response
    		delete(md.HeaderMD, "x-http-code")
    		delete(w.Header(), "Grpc-Metadata-X-Http-Code")
    		w.WriteHeader(code)
    	}
    
    	return nil
    }
    
    ...
    gwMux := runtime.NewServeMux(
    	runtime.WithForwardResponseOption(httpResponseModifier),
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

# 3. 修改默认响应body和error

  • grpc-gw 出错时默认返回格式

    {
      "code":5, 
      "message":"Not Found", 
      "details":[]
    }
    
    1
    2
    3
    4
    5

# 3.1 方案一

  • 将返回的内容定义为每条消息中回复的一部分

    message HelloWorldWrapper {
      int code = 1;
      HelloWorldResponse data = 2;
      string error = 3;
    }
    
    message HelloWorldResponse {
      string key = 1;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 缺点是大量的proto api需要冗余定义

# 3.2 方案二

  • 核心函数

    • runtime.WithForwardResponseOption只会在正常响应的时候调用
    • runtime.WithErrorHandler该函数只要请求就会调用,且在正常请求runtime.WithForwardResponseOption之后调用
  • 代码示例

    • custom res

      type StandardResp struct {
      	Code  int         `json:"code"`
      	Data  interface{} `json:"data"`
      	Error string      `json:"error"`
      }
      
      const (
      	proxyFlag = "__success__"
      )
      
      func HttpSuccessHandler(ctx context.Context, w http.ResponseWriter, p proto.Message) error {
      	fmt.Print("111111111111111")
      	resp := StandardResp{
      		Code:  0,
      		Data:  p,
      		Error: "",
      	}
      	bs, _ := json.Marshal(&resp)
      	return errors.New(proxyFlag + string(bs))
      }
      
      func HttpErrorHandler(ctx context.Context, mux *runtime.ServeMux, m runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) {
      	fmt.Print("00000000000000")
      	w.Header().Set("Content-Type", "application/json")
      
      	// success proxy
      	raw := err.Error()
      	if strings.HasPrefix(raw, proxyFlag) {
      		raw = raw[len(proxyFlag):]
      		w.Write([]byte(raw))
      		return
      	}
      
      	// normal error
      	s, ok := status.FromError(err)
      	if !ok {
      		s = status.New(codes.Unknown, err.Error())
      	}
      	resp := StandardResp{
      		Code:  1,
      		Data:  nil,
      		Error: s.Message(),
      	}
      	bs, _ := json.Marshal(&resp)
      	w.Write(bs)
      }
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
    • mux

      	// gateway
      	gwMux := runtime.NewServeMux(
      		runtime.WithForwardResponseOption(HttpSuccessHandler),
      		runtime.WithErrorHandler(HttpErrorHandler),
      	)
      
      1
      2
      3
      4
      5

# 自定义错误返回

# 1. 定义error response 格式

  • demo

    type StandardResp struct {
    	Code  int         `json:"code"`
    	Data  interface{} `json:"data"`
    	Error string      `json:"error"`
    }
    
    const (
    	proxyFlag = "__success__"
    )
    
    func HttpSuccessHandler(ctx context.Context, w http.ResponseWriter, p proto.Message) error {
    	fmt.Print("111111111111111")
    	resp := StandardResp{
    		Code:  0,
    		Data:  p,
    		Error: "",
    	}
    	bs, _ := json.Marshal(&resp)
    	return errors.New(proxyFlag + string(bs))
    }
    
    func HttpErrorHandler(ctx context.Context, mux *runtime.ServeMux, m runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) {
    	fmt.Print("00000000000000")
    	w.Header().Set("Content-Type", "application/json")
    
    	// success proxy
    	raw := err.Error()
    	if strings.HasPrefix(raw, proxyFlag) {
    		raw = raw[len(proxyFlag):]
    		w.Write([]byte(raw))
    		return
    	}
    
    	// normal error
    	s, ok := status.FromError(err)
    	if !ok {
    		s = status.New(codes.Unknown, err.Error())
    	}
    	resp := StandardResp{
    		Code:  1,
    		Data:  nil,
    		Error: s.Message(),
    	}
    	bs, _ := json.Marshal(&resp)
    	w.Write(bs)
    }
    ...
    	// gateway
    	gwMux := runtime.NewServeMux(
    		runtime.WithForwardResponseOption(HttpSuccessHandler),
    		runtime.WithErrorHandler(HttpErrorHandler),
    	)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52

# 2. 自定义路由错误处理

  • 在 *runtime.ServeMux 由于路由问题而无法为请求提供服务时定义错误行为,请使用 runtime.WithRoutingErrorHandler

  • 通过这个错误处理程序,将配置所有的HTTP路由错误。默认行为是将HTTP错误代码映射到gRPC错误,HTTP 状态及其到 gRPC 状态的映射:

    • HTTP 404 Not Found -> gRPC 5 NOT_FOUND
    • HTTP 405 Method Not Allowed -> gRPC 12 UNIMPLEMENTED
    • HTTP 400 Bad Request -> gRPC 3 INVALID_ARGUMENT
  • demo

    func handleRoutingError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
    	if httpStatus != http.StatusMethodNotAllowed {
    		runtime.DefaultRoutingErrorHandler(ctx, mux, marshaler, writer, request, httpStatus)
    		return
    	}
    
    	// Use HTTPStatusError to customize the DefaultHTTPErrorHandler status code
    	err := &HTTPStatusError{
    		HTTPStatus: httpStatus
    		Err:        status.Error(codes.Unimplemented, http.StatusText(httpStatus))
    	}
    
    	runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w , r, err)
    }
    ....
    mux := runtime.NewServeMux(
    	runtime.WithRoutingErrorHandler(handleRoutingError),
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

# 获取 HTTP Path pattern

  • proto

    syntax = "proto3";
    option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/proto/examplepb";
    package grpc.gateway.examples.internal.proto.examplepb;
    
    import "google/api/annotations.proto";
    
    service LoginService {
      rpc Login (LoginRequest) returns (LoginReply) {
        option (google.api.http) = {
            post: "/v1/example/login"
            body: "*"
        };
      }
    }
    
    message LoginRequest {}
    
    message LoginReply {}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • main.go

    mux := runtime.NewServeMux(
      runtime.WithMetadata(func(ctx context.Context, r *http.Request) metadata.MD {
        md := make(map[string]string)
        if method, ok := runtime.RPCMethod(ctx); ok {
          md["method"] = method // /grpc.gateway.examples.internal.proto.examplepb.LoginService/Login
        }
        if pattern, ok := runtime.HTTPPathPattern(ctx); ok {
          md["pattern"] = pattern // /v1/example/login
        }
        return metadata.New(md)
      }),
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
#grpc
上次更新: 2023/05/04, 15:30:48
【grpc】11.grpc-gw将gRPC 转 RESTful api
【protobuf】protobuf 进阶

← 【grpc】11.grpc-gw将gRPC 转 RESTful api 【protobuf】protobuf 进阶→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式