因此,我们有这个“代理”应用程序,它既是针对Angular应用程序的服务,又是使用WCF服务的客户端。
来自UI的请求调用了代理webApi,后者又调用了WCF(实际上是多个源,但可以简化为一个基础服务)。 WCF服务可能崩溃,并因此返回非OK HTTP状态代码。 WCF服务是第三方,因此绝对没有办法对其进行更改。
我们想在代理应用程序中拦截它,但是我们不能。 我尝试过制作中间件,过滤器属性,异常处理程序以及消息检查器的自定义行为。没运气。
发生的情况是,基础服务崩溃后,它将导致代理应用程序中出现异常。 我们的异常处理程序中间件捕获了此错误,但是我们无权访问WCF服务返回的HTTP状态代码。 只有异常消息可以指出发生了什么问题,但是我们不想依靠解析文本消息。 这是异常消息的一个示例> “远程服务器返回了意外的响应:(405)不允许使用方法。”
我还尝试调用自己创建的基础webApi-只是为了了解在拦截基础响应HTTP状态代码时WCF调用和webApi调用之间是否存在差异。 相同但不同:) 看起来像是在正确的位置挂上HTTP堆栈管道?!
因此,调试代理应用程序时,我们会遇到以下情况:
在下面的代码中注意我的评论;)
控制器:
[WcfErrorHandling]
public async Task<PersonCivilIdentifierLookupResponse1> GetKRAsync(
PersonCivilIdentifierLookupRequest lookupRequest)
{
if (lookupRequest == null)
{
throw new ArgumentNullException(nameof(lookupRequest));
}
PersonCivilIdentifierLookup lookup = new PersonCivilIdentifierLookup
{ PersonCivilIdentifierLookupRequest = lookupRequest };
var binding = new WSHttpBinding();
var endpoint = CreateEndpoint("***"); // URI for the underlying service removed
var channelFactory = new ChannelFactory<MethodsPortType>(binding, endpoint);
var behavior = new MessageInspectorBehavior(new MessageInspector());
channelFactory.Endpoint.EndpointBehaviors.Add(behavior);
var client = channelFactory.CreateChannel();
// originally just used auto-generated client, but in order to add custom behavior
// i created custom client via channelFactory above
//using (var client = new MethodsPortTypeClient(binding, endpoint))
{
var response = await client.PersonCivilIdentifierLookupAsync(
new PersonCivilIdentifierLookupRequest1(lookup));
return response;
}
}
消息检查器:
public class MessageInspector : IClientMessageInspector
{
int _statusCode;
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// will never go here when WCF service returns anything other that HTTP status code 200
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
属性:
// seams to be totally ignored, or the exception is thrown before execution gets here
public class WcfErrorHandlingAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext context)
{
var statusCode = context.HttpContext.Response.StatusCode;
base.OnResultExecuted(context);
}
public override void OnResultExecuting(ResultExecutingContext context)
{
base.OnResultExecuting(context);
}
}
中间件异常处理程序:
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (FaultException fault)
{
}
catch (Exception ex)
{
var logger = LogManager.GetCurrentClassLogger();
LogUtils.AddPropertiesToLogger(logger, httpContext);
logger.Error(ex);
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var feature = context.Features.Get<IExceptionHandlerPathFeature>();
var x = feature?.Error;
return context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = ex?.Message
}.ToString());
}
}
启动:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// tried the UseStatusCodePages as shown below
// but it's not intercepting the response from WCF service before exception is thrown
//app.UseStatusCodePages(builder =>
//{
// builder.Run(async context =>
// {
// var response = context.Response;
// var statusCode = response.StatusCode;
// var headers = response.Headers.ToDictionary(x => x.Key, x => x.Value.ToString());
// if (statusCode > 299)
// {
// await Task.FromException(new ApiException("KR Service har returned an error", statusCode, response.ToString(), headers, null));
// }
// });
//});
app.ConfigureExceptionHandler();
app.UseMvc();
}
在传统的.NET Framework中,我想我们可以创建一个从WebHttpBehavior继承并使用WebHttpBinding的行为类,但是在Core中它们已经消失了。
NB。我们使用Core 2.2(可能会在不久的将来升级,但我看不到Core 3.0 / 3.1可以解决此问题!?)