Blazor WebAssembly发布IIS调用gRPC报Grpc.Core.ClientBase错误
问题描述
应用运行时在浏览器控制台抛出如下异常,导致gRPC API调用失败:
System.ArgumentException: The base-type Grpc.Core.ClientBase for service-proxy CalibrationSaaS.Application.Services.IBasicsServices`1[ProtoBuf.Grpc.CallContext] lacks a suitable CallInvoker API
异常触发位置为创建gRPC服务代理的代码行,本地IDE调试环境运行无任何异常,仅发布部署到IIS环境后稳定复现。
触发异常的代码片段:
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); using (var channel = Grpc.Net.Client.GrpcChannel.ForAddress(builder.Configuration["Kestrel:Endpoints:Http3:Url"], new GrpcChannelOptions { HttpClient = httpClient })) { var a = channel.CreateGrpcService<Application.Services.IBasicsServices<CallContext>>(); // 错误在该行触发 var b = await a.GetRoles(CallContext.Default); // 该方法已实现 return b; }
常见触发原因及修复方案
IL裁剪导致必要元数据丢失(最高概率)
Blazor WebAssembly发布时默认启用IL裁剪功能缩减程序集体积,本地调试模式默认关闭裁剪因此不会触发问题。如果裁剪器误删了Grpc核心库、ProtoBuf.Grpc库或者自定义服务契约程序集中ClientBase对应的构造函数、CallInvoker相关成员,就会抛出该异常。
验证方式:在项目csproj文件中临时添加<PublishTrimmed>false</PublishTrimmed>配置后重新发布,如果异常消失即可确认是裁剪问题。
修复方式:不需要全量关闭裁剪,只需将相关程序集添加为裁剪根程序集,避免被误裁剪,参考配置:<ItemGroup> <TrimmerRootAssembly Include="ProtoBuf.Grpc" /> <TrimmerRootAssembly Include="Grpc.Net.Client" /> <TrimmerRootAssembly Include="Grpc.Core.Api" /> <TrimmerRootAssembly Include="CalibrationSaaS.Application" /> </ItemGroup>发布文件缺失或版本不匹配
如果采用增量发布覆盖IIS部署目录,可能出现旧版本dll残留、部分gRPC相关依赖dll未正确上传的情况,导致运行时加载的程序集版本和开发环境不一致,找不到匹配的API签名。
修复方式:清空IIS站点对应的部署目录,执行全量发布后重新上传所有文件,核对部署目录中Grpc、ProtoBuf相关dll的版本和本地开发环境的NuGet包版本完全一致。IIS层gRPC-Web配置错误
Blazor WebAssembly运行在浏览器环境,只能通过gRPC-Web协议调用服务,如果IIS未开启HTTP/2支持、配置了错误的URL重写规则拦截了gRPC请求、自定义错误页将gRPC响应替换为HTML格式内容,会导致客户端代理初始化时拿到非法响应,触发类型加载异常。
修复方式:确认IIS站点绑定开启HTTP/2,gRPC路由规则未被重写拦截,关闭站点的自定义错误页对gRPC请求路径的处理。程序集混淆导致类型匹配失败
如果发布流程开启了dotnet混淆工具,且未将gRPC服务契约接口、CallContext相关类型排除混淆,混淆后的类型重命名会导致运行时无法找到匹配的服务代理类型,抛出API缺失错误。
修复方式:在混淆配置中将所有gRPC服务接口、ProtoBuf契约类型、Grpc核心库类型排除混淆,禁止重命名这些类型。
内容的提问来源于stack exchange,提问作者Jav

