如何解决浏览器向gRPC服务器发送数据时的net::ERR_INVALID_HTTP_RESPONSE错误
问题原因及解决方案
问题描述
向http://localhost:50051/helloworld.Greeter/SayHello发送请求时,浏览器控制台报错:
POST http://localhost:50051/helloworld.Greeter/SayHello net::ERR_INVALID_HTTP_RESPONSE
....
(index):49 Error: TypeError: Failed to fetch
服务器无任何报错输出。
核心原因
当前搭建的是原生gRPC服务器,它基于HTTP/2的二进制协议通信;但浏览器端用fetch发送的是HTTP/1.1协议的请求,两者协议不兼容。gRPC服务器返回的响应不符合HTTP/1.1规范,导致浏览器判定为无效HTTP响应。
解决方案
浏览器原生不支持gRPC协议,推荐使用gRPC-Web实现浏览器与gRPC服务的通信,以下是具体实现步骤:
步骤1:调整服务端,添加gRPC-Web代理
安装所需依赖:
npm install @grpc/grpc-js @grpc/proto-loader grpc-web express
替换原server.mjs代码,同时启动gRPC原生服务和gRPC-Web代理:
import grpc from "@grpc/grpc-js"; import protoLoader from "@grpc/proto-loader"; import path from "path"; import { fileURLToPath } from "url"; import express from "express"; import { grpcWebMiddleware } from "grpc-web"; const PROTO_PATH = path.join(process.cwd(), "./helloworld.proto"); const packageDefinition = protoLoader.loadSync(PROTO_PATH, {}); const helloProto = grpc.loadPackageDefinition(packageDefinition).helloworld; // gRPC服务逻辑 function sayHello(call, callback) { callback(null, { message: "Hello " + call.request.name }); } // 启动原生gRPC服务(端口50052) const grpcServer = new grpc.Server(); grpcServer.addService(helloProto.Greeter.service, { sayHello }); grpcServer.bindAsync( "0.0.0.0:50052", grpc.ServerCredentials.createInsecure(), () => { grpcServer.start(); console.log("原生gRPC服务运行于 http://0.0.0.0:50052"); } ); // 启动Express服务作为gRPC-Web代理(端口50051) const app = express(); app.use(express.raw({ type: "application/x-protobuf" })); app.use(grpcWebMiddleware({ backend: "grpc://localhost:50052" })); app.listen(50051, () => { console.log("gRPC-Web代理运行于 http://0.0.0.0:50051"); });
步骤2:修改客户端,使用gRPC-Web SDK
替换原index.html代码,引入gRPC-Web库并创建客户端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>gRPC Client</title> <script src="https://cdn.jsdelivr.net/npm/protobufjs/dist/protobuf.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/grpc-web@1.5.0/dist/grpc-web.min.js"></script> </head> <body> <script> const protoPath = "helloworld.proto"; protobuf.load(protoPath, (err, root) => { if (err) throw err; // 创建gRPC-Web客户端 const greeterService = root.lookupService("helloworld.Greeter"); const client = new grpc.GrpcWebClientBase({ host: "http://localhost:50051" }); // 构造并发送请求 const request = root.lookupType("helloworld.HelloRequest").create({ name: "World" }); const requestBuffer = root.lookupType("helloworld.HelloRequest").encode(request).finish(); client.unary( greeterService.methods.sayHello, { request: requestBuffer, metadata: {} }, (err, response) => { if (err) { console.error("请求失败:", err); return; } const reply = root.lookupType("helloworld.HelloReply").decode(response.message); console.log("响应结果:", reply.message); } ); }); </script> </body> </html>
步骤3:启动并验证
- 启动服务端:
node server.mjs - 启动客户端静态服务:
npx http-server -o - 打开浏览器控制台,即可看到
响应结果: Hello World的输出。
内容的提问来源于stack exchange,提问作者Jon You
相关产品推荐
相关产品推荐

