HTTP状态码为401时,IErrorHandler返回错误的消息正文 [英] IErrorHandler returning wrong message body when HTTP status code is 401 Unauthorized

查看:98
本文介绍了HTTP状态码为401时,IErrorHandler返回错误的消息正文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了IErrorHandler来处理在我的宁静WCF服务的构造函数中引发的授权异常.捕获一般异常后,将按预期返回我的自定义类型,但是ContentType标头不正确.

I have implemented IErrorHandler to handle authorization exceptions thrown within the constructor of my restful WCF service. When a general exception is caught my custom type is returned as expected, but the ContentType header is incorrect.

HTTP/1.1 500 Internal Server Error
Content-Type: application/xml;
...

{"ErrorMessage":"Error!"}

但是,当错误处理程序尝试返回401未经授权的http状态代码时,消息主体将被覆盖为默认类型,但ContentType标头应为应有的状态.

However when the error handler tries to return a 401 Unauthorized http status code the message body is overridden to the default type but the ContentType header is as it should be.

HTTP/1.1 401 Unauthorized
Content-Type: application/json; 
...

{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"}

显然这里有问题,但我不确定是什么.

Obviously something is wrong here, but I'm not sure what.

如何实现IErrorHandler,以便它以正确的标头在json中返回我的自定义类型?

BaseDataResponseContract对象:

BaseDataResponseContract Object:

[Serializable]
[DataContract( Name = "BaseDataResponseContract" )]
public class BaseDataResponseContract
{
    [DataMember]
    public string ErrorMessage { get; set; }

} // end

这是我要返回的对象.我的应用程序中的所有其他对象都继承自该对象.当引发异常时,我们真正关心的就是http状态代码和错误消息.

This is the object I want to return. All of the other objects in my application inherit from this object. When an exception is thrown all we really care about is the http status code and the error message.

IErrorHandler实现(为简便起见,未显示日志):

IErrorHandler Implementation (logging is not shown for brevity):

namespace WebServices.BehaviorsAndInspectors
{
    public class ErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            return true;

        } // end

        public void ProvideFault(Exception ex, MessageVersion version, ref Message fault)
        {
            // Create a new instance of the object I would like to return with a default message
            var baseDataResponseContract = new BaseDataResponseContract { ErrorMessage = "Error!" };

            // Get the outgoing response portion of the current context 
            var response = WebOperationContext.Current.OutgoingResponse;

            // Set the http status code 
            response.StatusCode = HttpStatusCode.InternalServerError;

            // If the exception is a specific type change the default settings
            if (ex.GetType() == typeof(UserNotFoundException))
            {
                 baseDataResponseContract.ErrorMessage = "Invalid Username!";
                 response.StatusCode = HttpStatusCode.Unauthorized;
            }    

            // Create the fault message that is returned (note the ref parameter)
            fault = Message.CreateMessage(version, "", baseDataResponseContract, new DataContractJsonSerializer(typeof(BaseDataResponseContract)));

            // Tell WCF to use JSON encoding rather than default XML
            var webBodyFormatMessageProperty = new WebBodyFormatMessageProperty(WebContentFormat.Json);
            fault.Properties.Add(WebBodyFormatMessageProperty.Name, webBodyFormatMessageProperty);

            // Add ContentType header that specifies we are using json 
            var httpResponseMessageProperty = new HttpResponseMessageProperty();
            httpResponseMessageProperty.Headers[HttpResponseHeader.ContentType] = "application/json";
            fault.Properties.Add(HttpResponseMessageProperty.Name, httpResponseMessageProperty);

        } // end

    } // end class

} // end namespace

IServiceBehavior实现:

IServiceBehavior Implementation:

namespace WebServices.BehaviorsAndInspectors
{
    public class ErrorHandlerExtensionBehavior : BehaviorExtensionElement, IServiceBehavior
    {
        public override Type BehaviorType
        {
            get { return GetType(); }
        }

        protected override object CreateBehavior()
        {
            return this;
        }

        private IErrorHandler GetInstance()
        {
            return new ErrorHandler();
        }

        void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } // end

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var errorHandlerInstance = GetInstance();

            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(errorHandlerInstance);
            }
        }

        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } // end

    } // end class

} // end namespace

Web.Config:

Web.Config:

<system.serviceModel>

    <services>      
      <service name="WebServices.MyService">
        <endpoint binding="webHttpBinding" contract="WebServices.IMyService" />
      </service>
    </services>

    <extensions>      
      <behaviorExtensions>        
        <!-- This extension if for the WCF Error Handling-->
        <add name="ErrorHandlerBehavior" type="WebServices.BehaviorsAndInspectors.ErrorHandlerExtensionBehavior, WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />      
      </behaviorExtensions>    
    </extensions>

    <behaviors>          
      <serviceBehaviors>        
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <ErrorHandlerBehavior />
        </behavior>     
      </serviceBehaviors>    
    </behaviors>

    ....
</system.serviceModel>

最后,在使用WebFaultException时,我看到了类似的行为.我的想法是,这是一些深埋的.Net恶作剧的结果.我选择实现IErrorHandler,以便可以捕获可能无法处理的任何其他异常.

Finally, I am seeing similar behavior when using WebFaultException. My thought is that this is the result of some deeply buried .Net shenanigans. I am choosing to implement IErrorHandler so that I can catch any other exceptions that may not be handled.

参考:

https: //msdn.microsoft.com/zh-CN/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.100).aspx

http://www.brainthud.com/cards/5218/25441/which-four-behavior-interfaces-exist-for-interacting-with -服务或客户端描述-方法的实现方法和

其他示例:

IErrorHandler似乎没有处理我在WCF中的错误..有什么想法吗?

如何使自定义WCF错误处理程序返回带有非正常http代码的JSON响应?

如何您是否为HttpClient请求设置了Content-Type标头?

推荐答案

在为此苦苦挣扎了整整一天之后,我发现这是由IIS设置引起的.

After struggling with this for almost a full day I discovered that this was caused by an IIS setting.

在IIS中的API项目下,在身份验证"菜单下,将表单身份验证"设置为启用".我关闭了此功能",上面的代码按预期开始工作.我发现这是由于团队中的另一个开发人员将代码放入web.config文件中,从而更改了IIS中的设置.具体来说:

Under my API project in IIS, under the Authentication menu I had 'Forms Authentication' set to 'Enabled'. I turned off this 'feature' and the code above started working as expected. I found that this was due to another developer on my team putting code within the web.config file that altered the settings in IIS. Specifically:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    ...
    <system.web>
        <authentication mode="Forms" />
    </system.web>
    ...
</configuration>

此外,通过使用WebOperationContext OutgoingResponse对象上的ContentType属性,我能够使Content-Type标头正确显示.

Further, I was able to get the Content-Type header to appear correctly by using the ContentType Property on the WebOperationContext OutgoingResponse object.

// Get the outgoing response portion of the current context
var response = WebOperationContext.Current.OutgoingResponse;

// Add ContentType header that specifies we are using JSON
response.ContentType = new MediaTypeHeaderValue("application/json").ToString();

这篇关于HTTP状态码为401时,IErrorHandler返回错误的消息正文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆