【问题标题】:How can one catch a Web api returning other than 200 OK, with a WCF client?使用 WCF 客户端如何捕获返回 200 OK 以外的 Web api?
【发布时间】:2019-01-21 18:07:20
【问题描述】:

我正在使用使用 wcf 客户端的工具调用 Web Api。我必须使用那个工具,所以我不能使用 WebRequest 或 WebClient 或任何其他代码。我既不能推断方法调用的 try/catch。

我可以做的是配置 WCF 客户端,所以如果我可以在其他地方捕获/检查状态代码(自定义行为/绑定/通道)就可以了。

我已经有一个调用方法(带有 webrequest)并获取令牌的行为。此标记用于我要调用的实际方法。该令牌可以随时失效,然后该方法将返回 401 Unauthorized,我需要获取一个新令牌并再次进行调用。不幸的是,我的行为中的 AfterReply 方法没有被命中,错误被缓存在“较低”级别。

如果我用谷歌搜索这个问题,会出现很多答案,但除了使用另一个客户之外,我没有找到任何其他答案。 WCF 堆栈/管道中必须有可以挂钩的地方。

有什么建议或其他方法吗?

【问题讨论】:

    标签: wcf


    【解决方案1】:

    收到回复后可以使用WebOperationContext查看状态码:

    WebOperationContext.Current.IncomingResponse.StatusCode
    

    【讨论】:

    • 感谢您的回复。我遇到的问题是我必须在某个地方连接到 WCF 堆栈才能得到答复。在我使用的行为中,我有 BeforeRequest 和 AfterReply,并且在它们之间发生异常并传播到客户端的方法调用,并传递我的代码。我需要拦截它。我试过 ApplyClientErrorHandler,但如果我理解正确的话,它只适用于 SOAP。
    【解决方案2】:

    对于一些未在服务器端执行的服务器错误(500,401),我们不可能在客户端捕获它并出现故障异常。
    How can I get WCF FaultException when server returns an HTTP 401?
    相反,对于某些通信错误,ArgumentException、InvalidOperationException。它可以被客户端捕获并出现故障异常。
    IErrorHandler 接口的使用方法可以参考下面的例子。

    class Program
        {
            static void Main(string[] args)
            {
                Uri uri = new Uri("http://localhost:1100");
                BasicHttpBinding binding = new BasicHttpBinding();
                using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
                {
                    ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService), binding, "");
                    ServiceMetadataBehavior smb;
                    smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
                    if (smb == null)
                    {
                        smb = new ServiceMetadataBehavior();
                        smb.HttpGetEnabled = true;
                        smb.HttpGetUrl = new Uri("http://localhost:1100/mex");
                        sh.Description.Behaviors.Add(smb);
                    }
                    MyEndpointBehavior bhv = new MyEndpointBehavior();
                    se.EndpointBehaviors.Add(bhv);
    
    
                    sh.Open();
                    Console.WriteLine("service is ready");
                    Console.ReadKey();
                    sh.Close();
                }
            }
        }
        [ServiceContract(ConfigurationName = "isv")]
        public interface IService
        {
            [OperationContract]
            string Delete(int value);
            [OperationContract]
            void UpdateAll();
        }
        [ServiceBehavior(ConfigurationName = "sv")]
        public class MyService : IService
        {
            public string Delete(int value)
            {
                if (value <= 0)
                {
                    WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
                    throw new ArgumentException("Parameter should be greater than 0");
                }
                return "Hello";
            }
    
            public void UpdateAll()
            {
                throw new InvalidOperationException("Operation exception");
            }
        }
        public class MyCustomErrorHandler : IErrorHandler
        {
            public bool HandleError(Exception error)
            {
                return true;
            }
    
            public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
            {
                FaultException faultException = new FaultException(error.Message);
                MessageFault messageFault = faultException.CreateMessageFault();
                fault = Message.CreateMessage(version, messageFault, error.Message);
    
            }
    }
        public class MyEndpointBehavior : IEndpointBehavior
        {
            public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
            {
                return;
            }
    
            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
                return;
            }
    
            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
            {
                MyCustomErrorHandler myCustomErrorHandler = new MyCustomErrorHandler();
                endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(myCustomErrorHandler);
            }
    
            public void Validate(ServiceEndpoint endpoint)
            {
                return;
            }
    }
    

    客户。

     class Program
        {
            static void Main(string[] args)
            {
                ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
                try
                {
                    client.Delete(-1);
                }
                catch (FaultException fault)
                {
                    Console.WriteLine(fault.Reason.GetMatchingTranslation().Text);
                }
            }
    }
    

    App.config

      <system.serviceModel>
            <bindings>
                <basicHttpBinding>
                    <binding name="BasicHttpBinding_IService" />
                </basicHttpBinding>
            </bindings>
            <client>
                <endpoint address="http://localhost:1100/" binding="basicHttpBinding"
                    bindingConfiguration="BasicHttpBinding_IService" contract="ServiceReference1.IService"
                    name="BasicHttpBinding_IService" />
            </client>
    </system.serviceModel>
    

    如果有什么我可以帮忙的,请随时告诉我。

    【讨论】:

    • 感谢您的回答。不幸的是,我无法控制服务,甚至无法控制客户端,只能控制客户端行为。我使用消息拦截器而不是检查器取得了一些成功,但还没有登陆。
    • 对于 500 和 401 没有到达服务器/服务代码的部分,我不太确定。我写了一个模拟服务,它只返回 http 状态 401,并且到达了那个代码。如果授权在 Web 服务器中,那么它不会命中服务代码,但我的情况不是这样,它是返回 401 的 REST 服务,而不是 Web 服务器。但在我的情况下,这并不重要,我只需要处理它。
    猜你喜欢
    • 1970-01-01
    • 2018-01-14
    • 2017-12-14
    • 2015-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    相关资源
    最近更新 更多