2016-07-06 41 views
8

我已经实现了IErrorHandler来处理在我宁静的WCF服务的构造函数中抛出的授权异常。当遇到一般异常时,我的自定义类型按预期返回,但ContentType标头不正确。当HTTP状态代码是401时,IErrorHandler返回错误的消息正文未授权

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

{"ErrorMessage":"Error!"} 

然而,当错误处理程序试图返回一个401未授权的HTTP状态代码消息体被覆盖到默认类型但ContentType标头是理所应当的。

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

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

很明显这里有些问题,但我不确定是什么。

我该如何实现IErrorHandler,使其返回带有正确标题的json中的自定义类型?

BaseDataResponseContract对象:

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

} // end 

这是我想要返回的对象。我的应用程序中的所有其他对象都从此对象继承。当抛出异常时,我们真正关心的是http状态码和错误信息。

IErrorHandler实施(未显示以表简明日志记录):

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接口实现:

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:

<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,以便可以捕获任何其他可能无法处理的异常。

参考:

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

http://www.brainthud.com/cards/5218/25441/which-four-behavior-interfaces-exist-for-interacting-with-a-service-or-client-description-what-methods-do-they-implement-and

其他实例:

IErrorHandler doesn't seem to be handling my errors in WCF .. any ideas?

How to make custom WCF error handler return JSON response with non-OK http code?

How do you set the Content-Type header for an HttpClient request?

回答

0

经过几乎整整一天的努力,我发现这是由IIS设置引起的。

在IIS中的API项目下,在Authentication菜单下,我将'Forms Authentication'设置为'Enabled'。我关闭了这个'功能',上面的代码开始按预期工作。我发现这是由于我的团队中的另一位开发人员将代码放在web.config文件中,这些文件改变了IIS中的设置。具体做法是:

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

而且,我能得到的ContentType标头通过使用contentType属性的WebOperationContext OutgoingResponse对象上正确显示。

// 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(); 
0

我不太清楚你的应用程序是如何实现的。根据你的描述,我建议使用visual studio来调试你的ErrorHandler来查看异常是否到达你的回调。

如果是,则以您想要的方式手动构建肥皂故障或响应。

如果没有,这意味着在到达您的服务操作之前发生异常,它可能会在通道堆栈中失败,在这种情况下,一个简单的方法是将额外的HttpModule添加到自定义或映射响应。或者您可以尝试在通道堆栈中自定义编码器。

+0

是的......但如何?我得到了与WebOperationContext.current.OutgoingResponse相同的progrem,但我没有Write方法,所以如何进行自定义响应? – DestyNova

0

根据你写的内容,你在服务实现的构造函数中抛出异常。因为WCF使用反射来创建你的服务实现,除非你的服务是Singleton,你将得到一个TargetInvocationException。

实施例(使用LINQPad):

void Main() 
{ 
    try 
    { 
     Activator.CreateInstance(typeof(Foo)); 
    } 
    catch(Exception e) 
    { 
     e.Message.Dump(); 
     e.GetType().Name.Dump(); 
    } 
} 

public class Foo 
{ 
    public Foo() 
    { 
     throw new AuthorizationFailedException(); 
    } 
} 

public class AuthorizationFailedException : Exception 
{ 

} 

基本上避免抛出基于业务逻辑在构造函数中的异常。只有这样才能处理编程错误。

相关问题