调用 HttpListener.GetContextAsync() 后如何正确取消请求?

问题描述

我正在努力以不产生 IO 异常的方式关闭 HttpListener。

看来我要么以不寻常的方式使用 HttpListener,要么遗漏了一些明显的东西。

我正在设置一个 HttpListener 来监听来自 OAuth2.0 登录重定向。 我正在尝试启动侦听器,然后打开 Web 浏览器控件进行登录,然后在执行重定向关闭侦听器。

代码尚未完成所有这些工作,因为我正在尝试首先修复此错误

如果我关闭登录窗口以中止登录,然后重试但再次中止,我会收到此错误

The I/O operation has been aborted because of either a thread exit or an application request

这是堆栈跟踪:

System.Net.HttpListener.EndGetContext(IAsyncResult asyncResult)
System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar,Func`2 endFunction,Action`1 endAction,Task`1 promise,Boolean requiresSynchronization)
--- End of stack trace from prevIoUs location where exception was thrown ---
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
BridgeIT.cOAuth2.VB$StateMachine_49_ListenForAuth.MoveNext() in xxx\cOAuth2.vb:line 235

我曾尝试改用 Begin/EndGetContext() 方法,但它们只是产生相同的结果。

我错过了什么?

这是从按钮单击事件调用的我的登录函数

Public Function Login(ByVal vsLoginUrl As String,ByVal vsClientID As String,ByVal voScopes As IEnumerable(Of String)) As Boolean
        Dim bSuccess As Boolean = False
        Dim oAR As AuthenticationResult = nothing
        Dim frm As frmOAuth2LoginDlg

        Dim sResponseType As String = "code"
        Dim sScope As String = String.Join(" ",voScopes)
        Dim sstate As String = gsPracticeID
        Dim sCodeChallenge As String = GenerateCodeChallenge()
        Dim sCodeChallengeMethod As String = "S256"

        'Append query string params
        vsLoginUrl = $"{vsLoginUrl}?response_type={sResponseType}&client_id={vsClientID}&redirect_uri={msRedirectURL}&scope={sScope}&state={sstate}&code_challenge={sCodeChallenge}&code_challenge_method={sCodeChallengeMethod}"
        Try
            'Listen for the the oAuth redirect
            ListenForAuth()

            'Open web browser control
            frm = New frmOAuth2LoginDlg(vsLoginUrl)
            frm.ShowDialog()

            'Do something after logging in

        Catch ex As Exception
            ShowError(Err.Number,Err.Description,ex)

        Finally
            CloseRedirectListener()
            dispoSEOfObject(frm)
        End Try

    End Function

和监听函数

Private Async Sub ListenForAuth()
        Dim oContext As HttpListenerContext
        Dim oRequest As HttpListenerRequest
        Dim oResponse As HttpListenerResponse

        moRedirectListener = New HttpListener
        moRedirectListener.Prefixes.Add(msRedirectURL)
        moRedirectListener.Start()

        Try
            oContext = Await moRedirectListener.GetContextAsync()

            oRequest = oContext.Request
            
            'Do something with the request

        Catch oex As ObjectdisposedException
            ' Safely ignore this if the listener has been closed
            ' Else rethrow the exception
            If oex.ObjectName <> "System.Net.HttpListener" Then
                Throw
            End If

        Catch ex As Exception
            ShowError(Err.Number,ex)
        End Try
    End Sub

和 CloseRedirectListener 函数

    Private Sub CloseRedirectListener()
        If moRedirectListener IsNot nothing Then
            moRedirectListener.Stop()
            moRedirectListener.Prefixes.Remove(msRedirectURL)
            moRedirectListener.Close()
        End If
    End Sub

解决方法

这个问题似乎没有太大变化,所以我会为其他人发布我的解决方案,直到提供更好的解决方案。

捕获特定异常是我发现异步处理取消请求的唯一方法:

Private Async Function ListenForAuth(frm As frmOAuth2LoginDlg) As Task
        Dim oContext As HttpListenerContext
        Dim oRequest As HttpListenerRequest

        moRedirectListener = New HttpListener

        moRedirectListener.Prefixes.Add(RedirectURL)
        moRedirectListener.Start()

        Try
            oContext = Await moRedirectListener.GetContextAsync()
            oRequest = oContext.Request

            ' Do something with the request

        ' Handle the exceptions in the case of cancelled requests
        Catch oObjectDisposedException As ObjectDisposedException
            ' If the Listener has been closed then safely ignore this
            ' Else rethrow
            If oObjectDisposedException.ObjectName <> "System.Net.HttpListener" Then
                Throw
            End If

        Catch oHttpListenerException As System.Net.HttpListenerException
            ' If the Listener has been stopped then safely ignore this
            ' Else rethrow
            If oHttpListenerException.Message <> "The I/O operation has been aborted because Of either a thread Exit Or an application request" Then
                Throw
            End If

        Catch ex As Exception
            ' Do something with other exceptions

        Finally
            If Application.OpenForms.OfType(Of frmOAuth2LoginDlg).Count > 0 Then
                frm.Close()
            End If

        End Try

    End Function

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...