Wcf 服务适用于 .NET Core 3.1 控制台应用程序,但无法适用于 ASP.NET Core 3.1 Web API

nin*_*ino 3 wcf winhttp asp.net-core-webapi .net-core-3.1 asp.net-core-3.1

在工作中遇到一个奇怪的问题。尝试连接到基于 https、自定义客户端证书的连接服务。我正在使用 BasicHttpsBinding 和 ChannelFactory 类为服务创建客户端。

能够连接到服务并在 3.1 控制台应用程序上获得响应,但是当我将相同的代码放入 3.1 Web API 控制器时,当我尝试使用客户端调用相同的操作时失败,并显示错误“没有端点听...。这可能是由于不正确的肥皂动作造成的”。内部异常表示“winhttp 异常。无法解析服务器名称或地址”

我尝试运行 fiddler 以查看有什么不同,对于控制台应用程序,我看到一条隧道正在建立到外部服务 URL,但是对于来自 Web API 的调用,我在 Fiddler 上看不到任何内容,这意味着它在 Web API 管道本身的某个地方失败了。

在 Google 上的搜索指向检查代理设置,因为这是在公司网络上,但netsh winhttp 显示代理 显示直接(无代理服务器),所以我认为这不是与公司代理相关的问题。

3.1 控制台应用程序和 Web API 之间的网络堆栈究竟有何不同,WCF 服务调用可能会在 Web API 中失败但在控制台应用程序中工作?

有问题的代码

var binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var endpoint = new EndpointAddress(new Uri("https://service url"));
var channelFactory = new ChannelFactory<ExternalServiceInterface>(binding, endpoint);
channelFactory.Credentials.ClientCertificate.Certificate = new X509Certificate2(certificatepath, password);

_client = channelFactory.CreateChannel();

var sbmtGtDtResp = _client.ExternalServiceOperation(data);
Run Code Online (Sandbox Code Playgroud)

删除了服务 url 等敏感信息,并替换了通过 ExternalServiceInterface 将连接服务添加到项目时生成的接口名称。 在 .NET Core 3.1 Web API中的 _client.ExternalServiceOperation() 处出现异常,但适用于 .NET Core 3.1 控制台应用程序

异常转储

{System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at "https://service url" that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
 ---> System.Net.Http.HttpRequestException: An error occurred while sending the request.
 ---> System.Net.Http.WinHttpException (80072EE7, 12007): The server name or address could not be resolved
   at System.Threading.Tasks.RendezvousAwaitable`1.GetResult()
   at System.Net.Http.WinHttpHandler.StartRequest(WinHttpRequestState state)
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.ServiceModelHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(HttpRequestException requestException, HttpRequestMessage request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
   at System.ServiceModel.Channels.RequestChannel.RequestAsync(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.RequestAsyncInternal(Message message, TimeSpan timeout)
   at System.Runtime.TaskHelpers.WaitForCompletionNoSpin[TResult](Task`1 task)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(MethodCall methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(MethodInfo targetMethod, Object[] args)
--- End of stack trace from previous location where exception was thrown ---
   at System.Reflection.DispatchProxyGenerator.Invoke(Object[] args)
   at generatedProxy_1.submitGetDataRequest(submitGetDataRequestRequest )
   at WebAPIPOC.Controllers.ServiceController.Process() in "Code filepath.cs":line 50
   at lambda_method(Closure , Object , Object[] )
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()}
Run Code Online (Sandbox Code Playgroud)

nin*_*ino 8

所以,就像往常一样,我真的很愚蠢。但是如果有人和我犯同样的错误,我会列出来。

System.ServiceModel.*Nuget 包更新到最新版本(从 4.4.0 -> 4.7.0)为我解决了这个问题,不知道为什么在添加 WCF/连接服务时会自动安装旧版本。

已经读到较新版本的 .NET Core 不再使用 WinHttp 而是使用 SocketsHttpHandler,这应该会提醒我使用旧库,因为我看到了 WinHttpException

至于为什么它在我的控制台应用程序中工作。我在那里也有 4.4.0 版本的库,但显然是为了修复一些参考错误,我手动安装了System.Private.ServiceModel它的依赖项System.ServiceModel,通常不需要手动安装。我安装了 4.7.0 版本的System.Private.ServiceModel. 这以某种方式使 WCF 服务调用在控制台应用程序中工作,即使System.Private.ServiceModelSystem.ServiceModel包之间的版本不匹配

导致我检查库版本的 Github 问题线程,详细说明了为什么旧版本的库会导致错误 - https://github.com/dotnet/wcf/issues/3311