gpt4 book ai didi

xml - VBA 如何捕获请求超时错误?

转载 作者:数据小太阳 更新时间:2023-10-29 01:42:10 33 4
gpt4 key购买 nike

我正在使用对象 MSXML2.ServerXMLHTTP60向网络服务发送请求;有了这个对象,我可以通过 加速数据加载异步 方法和避免锁定 Excel 屏幕(无响应)。但是,我在 webservice 响应很长时间时仍然有问题,超出 ServerXMLHTTP60 超时设置,请求功能是静默的,我无法捕获超时错误。在 another question , @osknows 建议使用 xmlhttp status = 408捕捉超时错误,但它对我不起作用。

我准备了一个测试文件,大家可以下载at here .按 Atl + F8 打开 VBA 源代码, 你会看到类模块 CXMLHTTPHandler ,我从 this guide 复制的

    If m_xmlHttp.readyState = 4 Then
If m_xmlHttp.Status = 200 Then
MsgBox m_xmlHttp.responseText
ElseIf m_xmlHttp.Status = 408 Then 'Debug never run to here?
MsgBox "Request timeout"
Else
'Error happened
End If
End If

VBA 如何捕获请求超时错误?

感谢您的帮助!

最佳答案

这里有几个并发症。

  • MSXML2.ServerXMLHTTP不公开 COM 可用事件。因此,使用 WithEvents 实例化对象并不容易。并附加到其 OnReadyStateChange事件。
    该事件在那里,但处理它的标准 VBA 方式不起作用。
  • 无法使用 VBA IDE 创建可以处理该事件的模块。
  • 您需要拨打 waitForResponse() 当您使用异步请求时(除了调用 setTimeouts() !)
  • 没有timeout事件。超时作为错误抛出。

  • 解决问题#1:

    通常,VBA 类模块(也适用于用户表单或工作表模块)允许您执行以下操作:

    Private WithEvents m_xhr As MSXML2.ServerXMLHTTP

    所以你可以定义一个这样的事件处理程序:

    Private Sub m_xhr_OnReadyStateChange()
    ' ...
    End Sub
    MSXML2.ServerXMLHTTP 并非如此.这样做将导致 Microsoft Visual Basic 编译错误:“对象不提供自动化事件”。

    显然,该事件没有导出供 COM 使用。有办法解决这个问题。
    onreadystatechange的签名阅读

    Property onreadystatechange As Object

    所以你可以分配一个对象。我们可以用 onreadystatechange 创建一个类模块方法和分配是这样的:

    m_xhr.onreadystatechange = eventHandlingObject

    但是,这不起作用。 onreadystatechange期望一个对象,每当事件触发时,都会调用对象本身,而不是我们定义的方法。 (对于 ServerXMLHTTP 实例,无法知道我们打算使用用户定义的 eventHandlingObject 的哪个方法作为事件处理程序)。

    我们需要一个可调用对象,即一个带有默认方法的对象(每个 COM 对象都可以有一个)。
    (例如: Collection 对象是可调用的,您可以说 myCollection("foo")myCollection.Item("foo") 的简写。)

    解决问题#2:

    我们需要一个具有默认属性的类模块。不幸的是,这些不能使用 VBA IDE 创建,但您可以使用文本编辑器创建它们。
  • 准备包含 onreadystatechange 的类模块VBA IDE 中的函数
  • 将其导出到 .cls通过右键单击文件
  • 在文本编辑器中打开它并在 onreadystatechange 下方添加以下行签名:Attribute OnReadyStateChange.VB_UserMemId = 0
  • 删除原始类模块并从文件中重新导入它。

  • 这会将修改后的方法标记为 Default .您可以在对象浏览器 (F2) 中看到一个小蓝点,它标志着默认方法:

    Default Method

    所以每次调用对象时,实际上是 OnReadyStateChange方法被调用。

    解决问题 #3:

    只需拨打 waitForResponse()send() .

    m_xhr.Send
    m_xhr.waitForResponse timeout

    在超时的情况下:如果您没有调用此方法,则请求将永远不会返回。如果你这样做了,在 timeout 之后会抛出一个错误。毫秒。

    解决问题#4:

    我们需要使用 On Error为方便起见,捕获超时错误并将其转换为事件的处理程序。

    放在一起

    这是我编写的 VB 类模块,它包装和处理 MSXML2.ServerXMLHTTP目的。另存为 AjaxRequest.cls并将其导入到您的项目中:

    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "AjaxRequest"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = False
    Attribute VB_Exposed = False
    Option Explicit

    Private m_xhr As MSXML2.ServerXMLHTTP
    Attribute m_xhr.VB_VarHelpID = -1
    Private m_isRunning As Boolean

    ' default timeouts. TIMEOUT_RECEIVE can be overridden in request
    Private Const TIMEOUT_RESOLVE As Long = 1000
    Private Const TIMEOUT_CONNECT As Long = 1000
    Private Const TIMEOUT_SEND As Long = 10000
    Private Const TIMEOUT_RECEIVE As Long = 30000

    Public Event Started()
    Public Event Stopped()
    Public Event Success(data As String, serverStatus As String)
    Public Event Error(data As String, serverStatus As String, xhr As MSXML2.ServerXMLHTTP)
    Public Event TimedOut(message As String)

    Private Enum ReadyState
    XHR_UNINITIALIZED = 0
    XHR_LOADING = 1
    XHR_LOADED = 2
    XHR_INTERACTIVE = 3
    XHR_COMPLETED = 4
    End Enum

    Public Sub Class_Terminate()
    Me.Cancel
    End Sub

    Public Property Get IsRunning() As Boolean
    IsRunning = m_isRunning
    End Property

    Public Sub Cancel()
    If m_isRunning Then
    m_xhr.abort
    m_isRunning = False
    RaiseEvent Stopped
    End If
    Set m_xhr = Nothing
    End Sub

    Public Sub HttpGet(url As String, Optional timeout As Long = TIMEOUT_RECEIVE)
    Send "GET", url, vbNullString, timeout
    End Sub

    Public Sub HttpPost(url As String, data As String, Optional timeout As Long = TIMEOUT_RECEIVE)
    Send "POST", url, data, timeout
    End Sub

    Private Sub Send(method As String, url As String, data As String, Optional timeout As Long)
    On Error GoTo HTTP_error

    If m_isRunning Then
    Me.Cancel
    End If

    RaiseEvent Started

    Set m_xhr = New MSXML2.ServerXMLHTTP60

    m_xhr.OnReadyStateChange = Me
    m_xhr.setTimeouts TIMEOUT_RESOLVE, TIMEOUT_CONNECT, TIMEOUT_SEND, timeout

    m_isRunning = True
    m_xhr.Open method, url, True
    m_xhr.Send data
    m_xhr.waitForResponse timeout

    Exit Sub

    HTTP_error:
    If Err.Number = &H80072EE2 Then
    Err.Clear
    Me.Cancel
    RaiseEvent TimedOut("Request timed out after " & timeout & "ms.")
    Resume Next
    Else
    Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
    End If
    End Sub

    ' Note: the default method must be public or it won't be recognized
    Public Sub OnReadyStateChange()
    Attribute OnReadyStateChange.VB_UserMemId = 0
    If m_xhr.ReadyState = ReadyState.XHR_COMPLETED Then
    m_isRunning = False
    RaiseEvent Stopped

    ' TODO implement 301/302 redirect support

    If m_xhr.Status >= 200 And m_xhr.Status < 300 Then
    RaiseEvent Success(m_xhr.responseText, m_xhr.Status)
    Else
    RaiseEvent Error(m_xhr.responseText, m_xhr.Status, m_xhr)
    End If
    End If
    End Sub

    注意行 m_xhr.OnReadyStateChange = Me ,它将 AjaxRequest 实例本身分配为事件处理程序,这可以通过标记 OnReadyStateChange() 来实现。作为默认方法。

    请注意 如果您更改 OnReadyStateChange()您需要再次执行导出/修改/重新导入例程,因为 VBA IDE 不保存“默认方法”属性。

    该类公开了以下接口(interface)
  • 方法:
  • HttpGet(url As String, [timeout As Long])
  • HttpPost(url As String, data As String, [timeout As Long])
  • Cancel()
  • 特性
  • IsRunning As Boolean
  • 事件
  • Started()
  • Stopped()
  • Success(data As String, serverStatus As String)
  • Error(data As String, serverStatus As String, xhr As MSXML2.ServerXMLHTTP)
  • TimedOut(message As String)

  • 在另一个类模块中使用它,例如在用户表单中,使用 WithEvents :

    Option Explicit

    Private WithEvents ajax As AjaxRequest

    Private Sub UserForm_Initialize()
    Set ajax = New AjaxRequest
    End Sub

    Private Sub CommandButton1_Click()
    Me.TextBox2.Value = ""

    If ajax.IsRunning Then
    ajax.Cancel
    Else
    ajax.HttpGet Me.TextBox1.Value, 1000
    End If
    End Sub

    Private Sub ajax_Started()
    Me.Label1.Caption = "Running" & Chr(133)
    Me.CommandButton1.Caption = "Cancel"
    End Sub

    Private Sub ajax_Stopped()
    Me.Label1.Caption = "Done."
    Me.CommandButton1.Caption = "Send Request"
    End Sub

    Private Sub ajax_TimedOut(message As String)
    Me.Label1.Caption = message
    End Sub

    Private Sub ajax_Success(data As String, serverStatus As String)
    Me.TextBox2.Value = serverStatus & vbNewLine & data
    End Sub

    Private Sub ajax_Error(data As String, serverStatus As String, xhr As MSXML2.ServerXMLHTTP)
    Me.TextBox2.Value = serverStatus
    End Sub

    进行您认为合适的增强。 AjaxRequest类只是回答这个问题的副产品。

    关于xml - VBA 如何捕获请求超时错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11607677/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com