8

安静な WCF サービスのコンストラクター内でスローされた承認例外を処理するために、IErrorHandler を実装しました。一般的な例外がキャッチされると、カスタム タイプが期待どおりに返されますが、ContentType ヘッダーが正しくありません。

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

{"ErrorMessage":"Error!"}

ただし、エラー ハンドラが 401 Unauthorized 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-実装と

その他の例:

IErrorHandler が WCF でエラーを処理していないようです。何かアイデアはありますか?

カスタムWCFエラーハンドラーがOK以外のhttpコードでJSON応答を返すようにする方法は?

HttpClient リクエストの Content-Type ヘッダーをどのように設定しますか?

4

3 に答える 3

0

あなたのアプリケーションがどのように実装されているかよくわかりません。あなたの説明に基づいて、Visual Studio を使用して ErrorHandler をデバッグし、例外がコールバックに到達するかどうかを確認することをお勧めします。

はいの場合は、必要な方法で SOAP 障害または応答を手動で作成します。

そうでない場合は、サービス操作に到達する前に例外が発生したことを意味します。チャネル スタックで既に失敗している可能性があります。この場合、簡単な方法は、カスタムに追加の HttpModule を追加するか、応答をマップすることです。または、チャンネル スタックでエンコーダーをカスタマイズしてみてください。

于 2016-07-06T20:04:46.457 に答える