You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter gRPC请求抛出“潜在CORS问题”错误求助排查

Flutter gRPC请求CORS错误排查求助

发起gRPC请求时遇到以下错误:
gRPC Error (code: 2, codeName: UNKNOWN, message: HTTP request completed without a status (potential CORS issue)

采用gRPC实现客户端与服务端通信,测试Ping-Pong示例时出现该CORS错误,已配置Envoy但问题依旧,以下是相关代码与配置:

PingPong服务函数(Go)

package products

import (
    "context"

    pb "github.com/yohanderson/cidtmi/api/pingpong" // protoc generated package
)

type Server struct {
    pb.UnimplementedPingPongServiceServer
}

func (s *Server) Ping(ctx context.Context, in *pb.EmptyMessage) (*pb.Pong, error) {
    return &pb.Pong{Message: "Pong"}, nil
}

Go服务端代码

func main() {
    dbinfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
        "localhost", 8000, "postgres", "xxxxxx", "database")
    db, err := sql.Open("postgres", dbinfo)
    if err != nil {
        log.Fatalf("failed to open database: %v", err)
    }
    defer db.Close()

    path := "0.0.0.0:8081"
    lis, err := net.Listen("tcp", path)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    log.Println("Server is listening on #{path}")

    s := grpc.NewServer()

    pingSrv := &pingPong.Server{}
    apiPingPong.RegisterPingPongServiceServer(s, pingSrv)

    appSrv := &serviceAppointments.Server{DB: db}
    apiAppointments.RegisterAppointmentsServer(s, appSrv)

    prodSrv := &serviceProducts.Server{DB: db}
    apiProducts.RegisterProductsServer(s, prodSrv)

    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

Envoy配置文件

admin:
    access_log_path: /tmp/admin_access.log
    address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
    listeners:
    - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route:
                  cluster: greeter_service
                  max_stream_duration:
                    grpc_timeout_header_max: 0s
              cors:
                allow_origin_string_match:
                - prefix: "*"
                allow_methods: GET, PUT, DELETE, POST, OPTIONS
                allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                max_age: "1728000"
                expose_headers: custom-header-1,grpc-status,grpc-message
          http_filters:
          - name: envoy.filters.http.grpc_web
          - name: envoy.filters.http.cors
          - name: envoy.filters.http.router
clusters:
     - name: greeter_service
     connect_timeout: 0.25s
     type: logical_dns
     http2_protocol_options: {}
     lb_policy: round_robin
     load_assignment:
      cluster_name: cluster_0
      endpoints:
        - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 0.0.0.0
                    port_value: 8081

Flutter前端代码

class GrpcService {
  PingPongServiceClient? pingPongClient;

  void init() {
    final channel =
        GrpcWebClientChannel.xhr(Uri.parse('http://192.168.1.110:8080'));

    pingPongClient = PingPongServiceClient(channel);
  }

  Future<Pong> ping() async {
    final emptyMessage = EmptyMessage();
    final response = await pingPongClient!.ping(emptyMessage);
    return response;
  }
}

按钮触发逻辑

ElevatedButton(onPressed: () async {
    final response = await grpcService.ping();
    print('Received: ${response.message}');
}, child: Text('verificar')),

完整错误栈

Error: gRPC Error (code: 2, codeName: UNKNOWN, message: HTTP request completed without a status (potential CORS issue), details: null, rawResponse: , trailers: {})

dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 
327:10  createErrorWithStack
dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 337:28            
_throw
dart-sdk/lib/core/errors.dart 120:5                                            
throwWithStackTrace
dart-sdk/lib/async/zone.dart 1386:11                                           
callback
dart-sdk/lib/async/schedule_microtask.dart 40:11                               
_microtaskLoop
dart-sdk/lib/async/schedule_microtask.dart 49:5                                
_startMicrotaskLoop
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 181:15           
<fn>     

排查与解决方法

  1. 调整Envoy过滤器顺序
    当前配置中grpc_web过滤器在cors之前,导致CORS校验无法优先处理预检请求。修改http_filters顺序为:

    http_filters:
    - name: envoy.filters.http.cors
    - name: envoy.filters.http.grpc_web
    - name: envoy.filters.http.router
    
  2. 修复Envoy集群配置错误
    clusters下的cluster_name与集群名称greeter_service不匹配,修改为:

    load_assignment:
     cluster_name: greeter_service
     endpoints:
       - lb_endpoints:
           - endpoint:
               address:
                 socket_address:
                   address: 0.0.0.0
                   port_value: 8081
    
  3. 修正Go服务端日志语法
    Go中字符串格式化需用%s而非#{path},修改日志行:

    log.Printf("Server is listening on %s", path)
    

    确保服务正确监听8081端口。

  4. 优化CORS配置(开发环境)
    若为开发阶段,可将allow_origin_string_match替换为更直接的配置:

    cors:
      allow_origins: ["*"]
      allow_methods: GET, PUT, DELETE, POST, OPTIONS
      allow_headers: keep-alive,user-agent,cache-control,content-type,x-grpc-web,grpc-timeout
      max_age: "1728000"
      expose_headers: grpc-status,grpc-message
    
  5. 验证网络连通性
    确认Flutter设备能访问192.168.1.110:8080,可通过浏览器打开该地址测试Envoy是否正常响应。


内容的提问来源于stack exchange,提问作者Levy Márquez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:35:57