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 不调用底层方法。