如何在异步ASP上定义客户端超时.NET web服务调用
本文关键字:NET 超时 web 服务 调用 客户端 定义 异步 ASP | 更新日期: 2023-09-27 18:00:29
今天我搜索了一段时间,找到了一个调用(外部)ASP的特定案例。NET web服务,具有以下要求:
- 调用必须异步完成
- 必须实现超时,因为web服务可能需要很长时间才能执行
在互联网和StackOverflow上,出现了许多关于这个主题的问题,但要么是过时的,要么是建议使用仅适用于同步调用的WebRequest.TimeOut
属性。
一种替代方案是使用CCD_ 2。在开始呼叫之前启动计时器,并在呼叫到达TimerCallback
时取消计时器。
然而,我认为应该对此类案件采取更为普遍的做法。遗憾的是,到目前为止找不到它。有人有在异步web服务调用上设置客户端超时的想法吗?
提前谢谢。
请检查你的app.config,它将有一些servicemodel的设置,并且它有各种可以配置的值。
当我添加新的服务引用时,我可以在app.config、中看到以下内容
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="HeaderedServiceSoap"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:10:00"
sendTimeout="00:01:00"
allowCookies="false"
bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536"
maxBufferPoolSize="524288"
maxReceivedMessageSize="65536"
messageEncoding="Text"
textEncoding="utf-8"
transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32"
maxStringContentLength="8192"
maxArrayLength="16384"
maxBytesPerRead="4096"
maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None"
proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName"
algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://localhost/MyService.asmx"
binding="basicHttpBinding"
bindingConfiguration="HeaderedServiceSoap"
contract="WSTest.HeaderedServiceSoap"
name="HeaderedServiceSoap" />
</client>
</system.serviceModel>
请再次尝试删除和添加引用,确保您的应用程序的目标框架是4.0,并且您正在添加服务引用(而不是Web引用)。
实际上,您不能总是将WebRequest.TimeOut
用于异步操作;至少不是针对抽象CCD_ 5类的所有实现者。例如,在msdn上有文档记载,当调用HttpWebRequest.BeginGetResponse
来启动异步操作时,会忽略此属性。它明确指出,TimeOut
属性被忽略,如果需要,实现超时行为是用户的责任。
在msdn上HttpWebRequest.BeginGetResponse
文档附带的示例代码中,ManualResestEvent allDone
与WaitOrTimerCallback
的组合使用如下:
IAsyncResult result = (IAsyncResult) myHttpWebRequest.BeginGetResponse(
new AsyncCallback(RespCallback), myRequestState);
// TimeoutCallback aborts the request if the timer fires.
ThreadPool.RegisterWaitForSingleObject (result.AsyncWaitHandle,
new WaitOrTimerCallback(TimeoutCallback),
myHttpWebRequest,
DefaultTimeout,
true);
// The response came in the allowed time. The work processing will happen in the
// callback function RespCallback.
allDone.WaitOne();
请参阅msdn上的完整示例。
最重要的是你必须自己实现这一点。
我做了一个小项目来演示如何做到这一点;这并不像我想象的那么简单,但是,那到底是什么呢?
这是整个项目,其中有一个Web服务,还有一个WPF中的客户端,它有一个按钮,可以在有和没有超时的情况下调用http://www.mediafire.com/file/3xj4o16hgzm139a/ASPWebserviceAsyncTimeouts.zip.我会在下面放一些相关的片段。我使用了代码中描述的DispatcherTimer类来进行超时;看起来这个对象显然是对WPF友好的,并且(如果不是全部的话)应该可以解决大多数可能遇到的同步问题。
注意:这可能可以通过WCF风格的"服务引用"来实现,然而,我无法找到方法,遇到了很多死胡同。我最终选择了一个老式的"Web参考"(您可以通过转到"添加服务参考…",选择"高级"按钮,然后选择"添加Web参考"来获得它。
我之所以使用helper类,是为了演示在有很多调用的情况下可以做些什么;如果我们没有这些,记录每件事会很快变得一团糟。此外,可能会得到一个更通用的版本,其中几乎所有的处理都可以在代码中完成,但由于在服务引用代码中使用泛型的方式,我花了大部分时间的WCF代码不适合这种处理。我只是快速地查看了web服务代码,看起来更有可能完成,但不幸的是,我没有足够的时间来处理这部分内容。如果你想让我再看一眼,我会看看我能做些什么。
现在开始表演!)
回调助手:AsyncCallHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// contains base classes for webservice calls
using System.ServiceModel;
// contains the DispatcherTimer class for callback timers
using System.Windows.Threading;
namespace ASPSandcastleWPFClient
{
/// <summary>
/// DispatcherTimer usage info thanks to:
///
/// Wildermuth, Shawn, "Build More Responsive Apps With The Dispatcher", MSDN Magazine, October 2007
/// Original URL: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
/// Archived at http://www.webcitation.org/605qBiUEC on July 11, 2011.
///
/// this class is not set up to handle multiple outstanding calls on the same async call;
/// if you wish to do that, there would need to be some sort of handling for multiple
/// outstanding calls designed into the helper.
/// </summary>
public class AsyncCallHelper
{
#region Static Defaults
private static TimeSpan myDefaultTimeout;
/// <summary>
/// default timeout for all instances of the helper; should different timeouts
/// be required, a member should be created that can override this setting.
///
/// if this is set to null or a value less than zero, the timout will be set
/// to TimeSpan.Zero, and the helper will not provide timeout services to the
/// async call.
/// </summary>
public static TimeSpan DefaultTimeout
{
get
{
return myDefaultTimeout;
}
set
{
if ((value == null) || (value < TimeSpan.Zero))
myDefaultTimeout = TimeSpan.Zero;
else
myDefaultTimeout = value;
}
}
#endregion
/// <summary>
/// creates an instance of the helper to assist in timing out on an async call
/// </summary>
/// <param name="AsyncCall">the call which is represented by this instance. may not be null.</param>
/// <param name="FailureAction">an action to take, if any, upon the failure of the call. may be null.</param>
public AsyncCallHelper(Action AsyncCall, Action FailureAction)
{
myAsyncCall = AsyncCall;
myFailureAction = FailureAction;
myTimer = new DispatcherTimer();
myTimer.Interval = DefaultTimeout;
myTimer.Tick += new EventHandler(myTimer_Tick);
}
/// <summary>
/// Make the call
/// </summary>
public void BeginAsyncCall()
{
myAsyncCall();
if (DefaultTimeout > TimeSpan.Zero)
{
myTimer.Interval = DefaultTimeout;
myTimer.Start();
}
}
/// <summary>
/// The client should call this upon receiving a response from the
/// async call. According to the reference given above, it seems that
/// the WPF will only be calling this on the same thread as the UI,
/// so there should be no real synchronization issues here.
///
/// In a true multi-threading situation, it would be necessary to use
/// some sort of thread synchronization, such as lock() statements
/// or a Mutex in order to prevent the condition where the call completes
/// successfully, but the timer fires prior to calling "CallComplete"
/// thus firing the FailureAction after the success of the call.
/// </summary>
public void CallComplete()
{
if ((DefaultTimeout != TimeSpan.Zero) && myTimer.IsEnabled)
myTimer.Stop();
}
private void myTimer_Tick(object sender, EventArgs e)
{
CallComplete();
if (myFailureAction != null)
myFailureAction();
}
/// <summary>
/// WPF-friendly timer for use in aborting "Async" Webservice calls
/// </summary>
private DispatcherTimer myTimer;
/// <summary>
/// The call to be made
/// </summary>
private Action myAsyncCall;
/// <summary>
/// What action the helper should take upon a failure
/// </summary>
private Action myFailureAction;
}
}
具有相关代码的MainWindow.xaml.cs
文件:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ASPSandcastleWPFClient.ASPSandcastleWebserviceClient;
namespace ASPSandcastleWPFClient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ASPSandcastleWebservice myClient = null;
private AsyncCallHelper myHelloWorldHelper = null;
public MainWindow()
{
InitializeComponent();
}
private void InitializeClient()
{
myClient = new ASPSandcastleWebservice();
myHelloWorldHelper =
new AsyncCallHelper
(
myClient.HelloWorldAsync,
HelloWorldTimeout
);
}
private void Window_Initialized(object sender, EventArgs e)
{
InitializeClient();
}
/// <summary>
/// this is called prior to making a call so that we do not end up with multiple
/// outstanding async calls
/// </summary>
private void DisableButtons()
{
btnStartAsyncCall.IsEnabled = false;
btnStartAsyncCallWithTimeout.IsEnabled = false;
}
/// <summary>
/// this is called after a result is received or the call is cancelled due to timeout
/// so that we know it's safe to make another call.
/// </summary>
private void EnableButtons()
{
btnStartAsyncCall.IsEnabled = true;
btnStartAsyncCallWithTimeout.IsEnabled = true;
}
private void btnStartAsyncCall_Click(object sender, RoutedEventArgs e)
{
DisableButtons();
// disable the timeout handling
AsyncCallHelper.DefaultTimeout = TimeSpan.Zero;
myClient.HelloWorldCompleted += new HelloWorldCompletedEventHandler(myClient_HelloWorldCompleted);
myHelloWorldHelper.BeginAsyncCall();
lblResponse.Content = "waiting...";
}
private void btnStartAsyncCallWithTimeout_Click(object sender, RoutedEventArgs e)
{
DisableButtons();
// enable the timeout handling
AsyncCallHelper.DefaultTimeout = TimeSpan.FromSeconds(10);
lblResponse.Content = "waiting for 10 seconds...";
myHelloWorldHelper.BeginAsyncCall();
}
/// <summary>
/// see note RE: possible multi-thread issues when not using WPF in AsyncCallHelper.cs
/// </summary>
private void HelloWorldTimeout()
{
myClient.CancelAsync(null);
lblResponse.Content = "call timed out...";
EnableButtons();
}
void myClient_HelloWorldCompleted(object sender, HelloWorldCompletedEventArgs e)
{
myHelloWorldHelper.CallComplete();
if (!e.Cancelled)
lblResponse.Content = e.Result;
EnableButtons();
}
}
}
我不知道它是否惯用,但当通过WebClient.DownloadStringAsync(...)
发出异步请求时,我也使用Silverlight中的计时器(DispatchTimer
)。
web服务返回什么?XML、JSON或其他?你是在用这个网站吗?如果是这样,为什么不尝试使用jquery-ajax调用,然后异步加载它,然后使用.ajax()指定超时。