【gRPC-gateway】option定义规则及HttpBody响应

HTTP Option 定义规则

在 .proto 文件中,通过 google.api.http 注解定义 HTTP 路由规则,控制请求参数映射

需要在.proto文件显式

import https://github.com/googleapis/googleapis/tree/master/google/api


一、HTTP Option 定义规则详解

1. 基础路由定义
核心属性说明
属性作用底层原理
HTTP 方法
(get/post/put/patch/delete)
定义 RESTful 接口的 HTTP 动作映射到 http.RequestMethod 字段,路由匹配时校验方法一致性
body指定 HTTP 请求体的映射规则决定将请求体中的 JSON 数据解析到 Protobuf 消息的哪个字段
response_body控制 HTTP 响应体的数据来源(默认返回完整消息,可指定子字段)序列化响应时仅提取指定字段,其他字段将被忽略

关键场景示例
场景 1:简单 GET 请求
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = {
get: "/v1/users/{user_id}"  // 路径参数 user_id 映射到 GetUserRequest.user_id
};
}

message GetUserRequest {
string user_id = 1;  // 必须与路径参数名称一致
}

请求映射逻辑

HTTP GET /v1/users/123 → GetUserRequest{user_id: "123"}

场景 2:POST 请求体映射
rpc CreateUser(CreateUserRequest) returns (User) {
option (google.api.http) = {
post: "/v1/users"
body: "user"  // 请求体映射到 CreateUserRequest.user 字段
};
}

message CreateUserRequest {
User user = 1;  // 接收请求体数据
}

请求示例

POST /v1/users
{
"name": "Alice",
"email": "alice@example.com"
}

映射结果

CreateUserRequest{
user: User{name: "Alice", email: "alice@example.com"}
}

场景 3:混合参数绑定
rpc UpdateUser(UpdateUserRequest) returns (User) {
option (google.api.http) = {
patch: "/v1/users/{id}"
body: "*"  // 整个请求体映射到 UpdateUserRequest
};
}

message UpdateUserRequest {
string id = 1;    // 来自路径参数
string name = 2;  // 来自请求体
int32 age = 3;    // 来自请求体
}

请求示例

PATCH /v1/users/456
{
"name": "Bob",
"age": 30
}

映射结果

UpdateUserRequest{
id: "456",
name: "Bob",
age: 30
}

2. body 属性的高级用法
规则对比表
语法行为适用场景
body: "*"整个请求体映射到 顶层消息对象简单请求,无额外路径/查询参数
body: "field"请求体映射到消息的 指定字段,其他字段从路径/查询参数获取混合参数请求(如更新操作)
body: ""禁用请求体映射,所有字段必须来自路径或查询参数GET/DELETE 等无 Body 请求

代码示例:body: "field" 的嵌套结构
rpc CreatePost(CreatePostRequest) returns (Post) {
option (google.api.http) = {
post: "/v1/posts"
body: "post_data"  // 请求体映射到 post_data 字段
};
}

message CreatePostRequest {
string author_id = 1;        // 必须通过查询参数传递 ?author_id=xxx
PostData post_data = 2;      // 来自请求体
}

message PostData {
string title = 1;
string content = 2;
}

请求示例

POST /v1/posts?author_id=789
{
"title": "Hello gRPC",
"content": "This is a tutorial..."
}

映射结果

CreatePostRequest{
author_id: "789",
post_data: PostData{
title: "Hello gRPC",
content: "This is a tutorial..."
}
}

3. response_body 的深度应用
默认行为 vs 指定字段
  • 未设置 response_body
rpc GetBook(GetBookRequest) returns (BookResponse) {
option (google.api.http) = {
get: "/v1/books/{id}"
};
}

message BookResponse {
Book book = 1;
Metadata meta = 2;
}

响应结果

{
"book": {...},
"meta": {...}
}
  • 设置 response_body: "book"
rpc GetBook(GetBookRequest) returns (BookResponse) {
option (google.api.http) = {
get: "/v1/books/{id}"
response_body: "book"  // 仅返回 book 字段
};
}

响应结果

{
"title": "gRPC Guide",
"author": "..."
}

典型应用场景
  1. 精简响应数据
    隐藏内部元数据字段(如分页信息、服务状态码)

  2. 直接返回子对象
    当响应消息包含包装层时,直接暴露核心数据

  3. 兼容旧版 API
    维持响应结构不变的情况下修改 Protobuf 定义


4. 特殊语法与边界条件
路径参数冲突处理
// ❌ 错误示例:路径参数与 body 字段同名
rpc ConflictExample(ConflictRequest) returns (Empty) {
option (google.api.http) = {
post: "/v1/test/{id}"
body: "*"
};
}

message ConflictRequest {
string id = 1;  // 同时来自路径参数和请求体,导致解析冲突
}

解决方案

  • 修改字段名称
  • 使用 body: "other_field" 避免覆盖

HttpBody 响应

HttpBody 是 gRPC-Gateway 中用于处理 非结构化响应数据 的核心机制。它允许直接返回二进制数据(如文件、图像、视频等),突破默认的 JSON 格式限制。


一、核心特性与使用场景
特性说明典型场景
原始二进制支持直接返回未经 JSON 序列化的数据文件下载(PDF、图片、音视频)
自定义 Content-Type可指定任意 MIME 类型(如 image/png返回特定格式数据(XML、CSV)
流式传输兼容可与 gRPC 流式结合使用(需自定义实现)大文件分块传输
低延迟处理避免 JSON 序列化/反序列化开销高性能二进制协议交互

二、Protobuf 定义详解
1. 基本定义格式
import "google/api/httpbody.proto";  // 必须导入

service FileService {
  // 返回 HttpBody 类型
  rpc DownloadFile(FileRequest) returns (google.api.HttpBody) {
    option (google.api.http) = {
      get: "/v1/files/{name}"
    };
  }
}
2. 关键字段说明

HttpBody 的 Protobuf 定义如下:

message HttpBody {
  string content_type = 1;  // 必须指定 MIME 类型
  bytes data = 2;           // 原始二进制数据
  map<string, string> extensions = 3;  // 扩展元数据(较少使用)
}

三、服务端实现(Go 示例)
1. 返回静态文件
func (s *FileServer) DownloadFile(ctx context.Context, req *pb.FileRequest) (*httpbody.HttpBody, error) {
  // 读取文件内容
  data, err := os.ReadFile("/path/to/files/" + req.Name)
  if err != nil {
    return nil, status.Error(codes.NotFound, "file not found")
  }

  // 构造 HttpBody 响应
  return &httpbody.HttpBody{
    ContentType: "application/pdf",  // 根据实际文件类型修改
    Data:        data,
  }, nil
}
2. 动态生成二进制数据
func (s *ChartService) GenerateChart(ctx context.Context, req *pb.ChartRequest) (*httpbody.HttpBody, error) {
  // 生成图表(示例使用伪代码)
  img := generatePNGChart(req.Data)
  
  return &httpbody.HttpBody{
    ContentType: "image/png",
    Data:        img.Bytes(),
  }, nil
}

四、客户端请求示例
1. 直接通过浏览器下载
# 访问 URL 触发文件下载
http://localhost:8080/v1/files/report.pdf
2. 使用 curl 获取二进制数据
curl -v http://localhost:8080/v1/files/image.jpg --output result.jpg
3. 前端 JavaScript 处理
fetch('/v1/files/image.jpg')
  .then(response => response.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'image.jpg';
    a.click();
  });

五、高级配置与技巧
1. 自定义 Content-Type 规则
// 根据文件扩展名动态设置 Content-Type
func getContentType(filename string) string {
  switch path.Ext(filename) {
    case ".pdf": return "application/pdf"
    case ".png": return "image/png"
    case ".csv": return "text/csv"
    default: return "application/octet-stream"
  }
}
2. 流式传输大文件

虽然 HttpBody 本身不支持流式,但可通过以下方式实现分块传输:

func (s *FileServer) StreamFile(req *pb.FileRequest, stream pb.FileService_StreamFileServer) error {
  file, _ := os.Open(req.Name)
  defer file.Close()

  buffer := make([]byte, 1024*1024) // 1MB 分块
  for {
    n, err := file.Read(buffer)
    if err == io.EOF {
      break
    }
    stream.Send(&httpbody.HttpBody{
      ContentType: "application/octet-stream",
      Data:        buffer[:n],
    })
  }
  return nil
}

六、注意事项与调试指南
1. 常见问题排查表
问题现象可能原因解决方案
返回数据被 JSON 编码未正确设置为 HttpBody 返回类型检查 .proto 文件导入和类型定义
Content-Type 未生效服务端未设置 content_type 字段确保在 HttpBody 中显式指定类型
中文文件名乱码未设置 Content-Disposition 头通过 Metadata 添加额外响应头
2. 添加响应头示例
// 在拦截器中设置响应头
func setDownloadHeader(ctx context.Context, w http.ResponseWriter, resp proto.Message) {
  if body, ok := resp.(*httpbody.HttpBody); ok {
    filename := "export.csv"
    w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
  }
}

// 注册到 ServeMux
mux := runtime.NewServeMux(
  runtime.WithForwardResponseOption(setDownloadHeader),
)

七、性能优化建议
  1. 启用 Gzip 压缩
    在网关层配置压缩中间件:

    handler := gziphandler.GzipHandler(mux)
    http.ListenAndServe(":8080", handler)
    
  2. 内存优化
    避免一次性加载大文件到内存,使用 io.Reader 流式处理:

    func streamFile(path string) (io.Reader, error) {
      return os.Open(path)
    }
    
  3. CDN 集成
    对于静态文件,直接返回重定向 URL:

    return &httpbody.HttpBody{
      ContentType: "text/plain",
      Data:        []byte("https://cdn.example.com/files/report.pdf"),
    }, nil
    

八、与普通响应的对比
特性普通响应(JSON)HttpBody 响应
数据格式强制 JSON 序列化保持原始二进制格式
Content-Typeapplication/json(固定)可自由定义(如 image/jpeg
元数据支持通过响应消息字段携带需通过 HTTP 头或自定义协议封装
性能开销有序列化/反序列化成本零转换开销(适合大文件)

https://github.com/0voice

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962129.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Autosar-Os是怎么运行的?(内存保护)

写在前面&#xff1a; 入行一段时间了&#xff0c;基于个人理解整理一些东西&#xff0c;如有错误&#xff0c;欢迎各位大佬评论区指正&#xff01;&#xff01;&#xff01; 1.功能概述 以TC397芯片为例&#xff0c;英飞凌芯片集成了MPU模块&#xff0c; MPU模块采用了硬件机…

什么是Maxscript?为什么要学习Maxscript?

MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…

(一)QT的简介与环境配置WIN11

目录 一、QT的概述 二、QT的下载 三、简单编程 常用快捷键 一、QT的概述 简介 Qt&#xff08;发音&#xff1a;[kjuːt]&#xff0c;类似“cute”&#xff09;是一个跨平台的开发库&#xff0c;主要用于开发图形用户界面&#xff08;GUI&#xff09;应用程序&#xff0c;…

vim交换文件的作用

1.数据恢复&#xff1a;因为vim异常的退出&#xff0c;使用交换文件可以恢复之前的修改内容。 2.防止多人同时编辑&#xff1a;vim检测到交换文件的存在,会给出提示&#xff0c;以避免一个文件同时被多人编辑。 &#xff08;vim交换文件的工作原理&#xff1a;vim交换文件的工作…

SpringCloudGateWay和Sentinel结合做黑白名单来源控制

假设我们的分布式项目&#xff0c;admin是8087&#xff0c;gateway是8088&#xff0c;consumer是8086 我们一般的思路是我们的请求必须经过我们的网关8088然后网关转发到我们的分布式项目&#xff0c;那我要是没有处理我们绕过网关直接访问项目8087和8086不也是可以&#xff1…

将多目标贝叶斯优化与强化学习相结合用于TinyML

论文标题 Combining Multi-Objective Bayesian Optimization with Reinforcement Learning for TinyML 作者信息 Mark Deutel, Friedrich-Alexander-Universitt Erlangen-Nrnberg, Germany Georgios Kontes, Fraunhofer IIS, Fraunhofer Institute for Integrated Circuits …

Big Bird:适用于更长序列的Transformer模型

摘要 基于Transformer的模型&#xff0c;如BERT&#xff0c;已成为自然语言处理&#xff08;NLP&#xff09;中最成功的深度学习模型之一。然而&#xff0c;它们的一个核心限制是由于其全注意力机制&#xff0c;对序列长度的二次依赖&#xff08;主要是在内存方面&#xff09;…

26_DropDown使用方法

创建下拉框DropDown 其中样板Template 是展示的选项框 其中Caption 是选中某个选项之后 展示的内容&#xff08;Caption Text 说明文字/Caption Image 说明图示&#xff09; 修改其 说明文字Caption Text 创建一个说明图示Image 设置为居左 而Item是 展示的选项框所展示的文字与…

【redis进阶】redis 总结

目录 介绍一下什么是 Redis&#xff0c;有什么特点 Redis 支持哪些数据类型 Redis 数据类型底层的数据结构/编码方式是什么 ZSet 为什么使用跳表&#xff0c;而不是使用红黑树来实现 Redis 的常见应用场景有哪些 怎样测试 Redis 服务器的连通性 如何设置 key 的过期时间 Redis …

AI大模型开发原理篇-1:语言模型雏形之N-Gram模型

N-Gram模型概念 N-Gram模型是一种基于统计的语言模型&#xff0c;用于预测文本中某个词语的出现概率。它通过分析一个词语序列中前面N-1个词的出现频率来预测下一个词的出现。具体来说&#xff0c;N-Gram模型通过将文本切分为长度为N的词序列来进行建模。 注意&#xff1a;这…

Linux工具使用

1.gcc/g的使用 1.1程序翻译的过程 ①预处理&#xff1a;展开头文件&#xff0c;替换宏&#xff0c;调节编译&#xff0c;去注释。 ②编译&#xff1a;将代码变成汇编语言 ③汇编&#xff1a;将汇编代码变成二进制不可执行的目标文件。 ④链接&#xff1a;将多个我写的多个…

后端token校验流程

获取用户信息 前端中只有 await userStore.getInfo() 表示从后端获取数据 在页面中找到info对应的url地址&#xff0c;在IDEA中查找 这里是getInfo函数的声明&#xff0c;我们要找到这个函数的使用&#xff0c;所以点getInfo() Override public JSONObject getInfo() {JSO…

Python 梯度下降法(二):RMSProp Optimize

文章目录 Python 梯度下降法&#xff08;二&#xff09;&#xff1a;RMSProp Optimize一、数学原理1.1 介绍1.2 公式 二、代码实现2.1 函数代码2.2 总代码 三、代码优化3.1 存在问题3.2 收敛判断3.3 函数代码3.4 总代码 四、优缺点4.1 优点4.2 缺点 Python 梯度下降法&#xff…

excel如何查找一个表的数据在另外一个表是否存在

比如“Sheet1”有“张三”、“李四”“王五”三个人的数据&#xff0c;“Sheet2”只有“张三”、“李四”的数据。我们通过修改“Sheet1”的“民族”或者其他空的列&#xff0c;修改为“Sheet2”的某一列。这样修改后筛选这个修改的列为空的或者为出错的&#xff0c;就能找到两…

2024年数据记录

笔者注册时间超过98.06%的用户 CSDN 原力是衡量一个用户在 CSDN 的贡献和影响力的系统&#xff0c;笔者原力值超过99.99%的用户 其他年度数据

7层还是4层?网络模型又为什么要分层?

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 一、为什么要分层 \quad 网络通信的复杂性促使我们需要一种分层的方法来理解和管理网络。就像建筑一样&#xff0c;我们不会把所有功能都混在一起…

JxBrowser 8.2.2 版本发布啦!

JxBrowser 8.2.2 版本发布啦&#xff01; • 已更新 #Chromium 至更新版本 • 实施了多项质量改进 &#x1f517; 点击此处了解更多详情。 &#x1f193; 获取 30 天免费试用。

论文阅读(十五):DNA甲基化水平分析的潜变量模型

1.论文链接&#xff1a;Latent Variable Models for Analyzing DNA Methylation 摘要&#xff1a; 脱氧核糖核酸&#xff08;DNA&#xff09;甲基化与细胞分化密切相关。例如&#xff0c;已经观察到肿瘤细胞中的DNA甲基化编码关于肿瘤的表型信息。因此&#xff0c;通过研究DNA…

【综合决策模型】考虑生命周期评估LCA 与多目标优化MOO的综合决策模型MOOLCA

目录 1. 概念和目的1.1 生命周期评估 (LCA, Life Cycle Assessment)1.2 多目标优化 (MOO, Multi-Objective Optimization)1.3 MOOLCA 的目标2. MOOLCA 的组成2.1 生命周期评估模块2.2 优化模块2.3 决策支持模块参考Life Cycle Assessment with Multi-Objective Optimization (M…

系统思考—蝴蝶效应

“个体行为的微小差异&#xff0c;可能在系统中引发巨大且不可预测的结果。” — 诺贝尔经济学得主托马斯谢林 我们常说&#xff0c;小变动带来大影响&#xff0c;这种现象&#xff0c;在复杂系统理论中被称为“蝴蝶效应”&#xff1a;即使极小的变化&#xff0c;也能在动态系…