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 
{ 

} 

基本上避免拋出基於業務邏輯在構造函數中的異常。只有這樣才能處理編程錯誤。

相關問題