WCF - 返回 HTTP 204 引发异常导致发送 body
WCF - Returning HTTP 204 throws exception causing a body to be sent
我已经使用 WCF 实现了 RESTful 服务。其中一项要求是该服务驻留在将使用它的网站的不同服务器(或至少端口)上。
为此,我实现了自定义消息检查器 - 这并不是我的工作,但我设法将来自几个不同来源的东西放在一起。一个是 here 另一个我现在找不到。
基本上这样我就可以说发件人的来源是允许的。此外,当浏览器发送 OPTIONS
请求时,它不会尝试执行该方法。
一切正常,除了当返回 204 时提琴手抱怨响应中有 body。经过检查,我发现抛出异常 -
The server encountered an error processing the request. The exception message is 'The communication object, System.ServiceModel.InstanceContext, cannot be used for communication because it has been Aborted.'.
堆栈跟踪:
at System.ServiceModel.Channels.CommunicationObject.ThrowIfClosedOrNotOpen()
at System.ServiceModel.InstanceContext.GetServiceInstance(Message message)
at System.ServiceModel.Dispatcher.InstanceBehavior.EnsureServiceInstance(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
密码是-
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequestHeader = request.Properties["httpRequest"] as HttpRequestMessageProperty;
if (httpRequestHeader.Method.ToUpper() == "OPTIONS" || httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
instanceContext.Abort();
}
return httpRequestHeader;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequestHeader = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponseHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
httpResponseHeader.Headers.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,pulse-symphonycrm-userloginid,pulse-symphonycrm-domainandusername");
httpResponseHeader.Headers.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
httpResponseHeader.Headers.Add("Access-Control-Allow-Credentials", "true");
string origin = httpRequestHeader.Headers["origin"];
if (origin != null)
{
httpResponseHeader.Headers.Add("Access-Control-Allow-Origin", origin);
}
if (httpRequestHeader.Method.ToUpper() == "OPTIONS")
{
httpResponseHeader.StatusCode = HttpStatusCode.NoContent;
}
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
httpResponseHeader.StatusDescription = "Unauthorized";
httpResponseHeader.StatusCode = HttpStatusCode.Unauthorized;
}
}
我认为在 instanceContext.Abort()
之后一定有一些操作导致抛出异常并生成 body,但无法追踪这是从哪里来的。
任何人都可以阐明这一点以及如何在保持正在发送的 headers 的同时阻止它。
正如我所怀疑和 Petar 提到的那样,instanceContext.Abort() 肯定是错误的根源,因为一旦我删除它,问题就消失了。
我最后采用的方法是简单地提供一个 OPTIONS 等效方法来调用,当浏览器发送这种类型的请求时 return 什么都不会 -
// Note the void return type
[WebInvoke(Method = "OPTIONS", UriTemplate = "/login")]
void OptionsLogin();
[WebInvoke(Method = "POST", UriTemplate = "/login", ResponseFormat = WebMessageFormat.Json)]
LogInResult LogIn(LogInInformation loginInformation);
此方法是对实际执行 return 的方法的补充。
但是我认为,如果您有许多最终会以这种方式被调用的方法,那么最好的方法是实现一个 OperationInvoker,正如 Petar 所提到的那样,并且 here 不调用底层方法。
我已经使用 WCF 实现了 RESTful 服务。其中一项要求是该服务驻留在将使用它的网站的不同服务器(或至少端口)上。
为此,我实现了自定义消息检查器 - 这并不是我的工作,但我设法将来自几个不同来源的东西放在一起。一个是 here 另一个我现在找不到。
基本上这样我就可以说发件人的来源是允许的。此外,当浏览器发送 OPTIONS
请求时,它不会尝试执行该方法。
一切正常,除了当返回 204 时提琴手抱怨响应中有 body。经过检查,我发现抛出异常 -
The server encountered an error processing the request. The exception message is 'The communication object, System.ServiceModel.InstanceContext, cannot be used for communication because it has been Aborted.'.
堆栈跟踪:
at System.ServiceModel.Channels.CommunicationObject.ThrowIfClosedOrNotOpen()
at System.ServiceModel.InstanceContext.GetServiceInstance(Message message)
at System.ServiceModel.Dispatcher.InstanceBehavior.EnsureServiceInstance(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
密码是-
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequestHeader = request.Properties["httpRequest"] as HttpRequestMessageProperty;
if (httpRequestHeader.Method.ToUpper() == "OPTIONS" || httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
instanceContext.Abort();
}
return httpRequestHeader;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequestHeader = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponseHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
httpResponseHeader.Headers.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,pulse-symphonycrm-userloginid,pulse-symphonycrm-domainandusername");
httpResponseHeader.Headers.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
httpResponseHeader.Headers.Add("Access-Control-Allow-Credentials", "true");
string origin = httpRequestHeader.Headers["origin"];
if (origin != null)
{
httpResponseHeader.Headers.Add("Access-Control-Allow-Origin", origin);
}
if (httpRequestHeader.Method.ToUpper() == "OPTIONS")
{
httpResponseHeader.StatusCode = HttpStatusCode.NoContent;
}
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
httpResponseHeader.StatusDescription = "Unauthorized";
httpResponseHeader.StatusCode = HttpStatusCode.Unauthorized;
}
}
我认为在 instanceContext.Abort()
之后一定有一些操作导致抛出异常并生成 body,但无法追踪这是从哪里来的。
任何人都可以阐明这一点以及如何在保持正在发送的 headers 的同时阻止它。
正如我所怀疑和 Petar 提到的那样,instanceContext.Abort() 肯定是错误的根源,因为一旦我删除它,问题就消失了。
我最后采用的方法是简单地提供一个 OPTIONS 等效方法来调用,当浏览器发送这种类型的请求时 return 什么都不会 -
// Note the void return type
[WebInvoke(Method = "OPTIONS", UriTemplate = "/login")]
void OptionsLogin();
[WebInvoke(Method = "POST", UriTemplate = "/login", ResponseFormat = WebMessageFormat.Json)]
LogInResult LogIn(LogInInformation loginInformation);
此方法是对实际执行 return 的方法的补充。
但是我认为,如果您有许多最终会以这种方式被调用的方法,那么最好的方法是实现一个 OperationInvoker,正如 Petar 所提到的那样,并且 here 不调用底层方法。