验证/刷新 Google API 访问令牌并避免 "file in use" 异常?
Authenticating / Refreshing Google API access token and avoiding a "file in use" exception?
这就是我使用 Google API:
进行身份验证的方式
Private Function DoAuthentication(ByRef rStrToken As String, ByRef rParameters As OAuth2Parameters) As Boolean
Dim credential As UserCredential
Dim Secrets = New ClientSecrets() With {
.ClientId = m_strClientID,
.ClientSecret = m_strClientSecret
}
m_Scopes.Add(CalendarService.Scope.Calendar)
Try
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(Secrets, m_Scopes,
"user", CancellationToken.None,
New FileDataStore("XXXXX.Application")).Result()
If credential.Token.IsExpired(Google.Apis.Util.SystemClock.Default) Then
credential.RefreshTokenAsync(CancellationToken.None)
End If
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "xxx"
}
m_Service = New CalendarService(initializer)
rStrToken = credential.Token.AccessToken.ToString()
rParameters.AccessToken = credential.Token.AccessToken
rParameters.RefreshToken = credential.Token.RefreshToken
Catch ex As Exception
' We encountered some kind of problem, perhaps they have not yet authenticated?
' Can we isolate that as the exception?
m_logger.Error(ex, "DoAuthentication")
Return False
End Try
Return True
End Function
然后在我的 Main
:
Dim parameters As New OAuth2Parameters
If (DoAuthentication(strToken, parameters)) Then
iResult = RESULT_SUCCESS_OAUTH
Else
Return RESULT_FAILED_OAUTH
End If
然后继续。问题是有时,即使“验证”成功,它也会开始 运行 其余代码。然后我得到一个例外:
2020-11-30 18:47:03.6762|ERROR|GoogleAuthandSync.Program|AddEventsToCalendarXML|System.IO.IOException: The process cannot access the file 'C:\Users\USERNAME\AppData\Roaming\XXXXX.Application\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user' because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
我如何验证/刷新令牌并确保我们已准备好继续以停止此异常?它并不总是发生。
(为了安全起见,我在上面的代码/错误文本中重命名了令牌文件)。
我应该使用 Await
关键字吗?
If credential.Token.IsExpired(Google.Apis.Util.SystemClock.Default) Then
Await credential.RefreshTokenAsync(CancellationToken.None)
End If
这会有什么不同吗?我注意到在这个 sample code 中他们使用 await
.
有人鼓励我在 GitHub 上询问这个问题(参见 discussion)。
事实证明,我不需要像以前那样使用 RefreshTokenAsync
。我能够简化我的代码并将其变成一个 Async
方法,例如:
Private Async Function DoAuthenticationAsync() As Task(Of Boolean)
Dim credential As UserCredential
Dim Secrets = New ClientSecrets() With {
.ClientId = m_strClientID,
.ClientSecret = m_strClientSecret
}
Try
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(Secrets, m_Scopes,
"user", CancellationToken.None,
New FileDataStore("XXXXX.Application"))
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "XXXXX"
}
m_Service = New CalendarService(initializer)
Catch ex As Exception
' We encountered some kind of problem, perhaps they have not yet authenticated?
' Can we isolate that as the exception?
m_logger.Error(ex, "DoAuthentication")
Return False
End Try
Return True
End Function
然后,我不得不将 Main
函数中的所有代码移动到一个任务中:
Private Async Function Run() As Task(Of Integer)
...
End Function
最后,我把Main
调整成这样:
Public Function Main() As Integer
Return RunAsync.Result
End Function
这就是我使用 Google API:
进行身份验证的方式Private Function DoAuthentication(ByRef rStrToken As String, ByRef rParameters As OAuth2Parameters) As Boolean
Dim credential As UserCredential
Dim Secrets = New ClientSecrets() With {
.ClientId = m_strClientID,
.ClientSecret = m_strClientSecret
}
m_Scopes.Add(CalendarService.Scope.Calendar)
Try
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(Secrets, m_Scopes,
"user", CancellationToken.None,
New FileDataStore("XXXXX.Application")).Result()
If credential.Token.IsExpired(Google.Apis.Util.SystemClock.Default) Then
credential.RefreshTokenAsync(CancellationToken.None)
End If
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "xxx"
}
m_Service = New CalendarService(initializer)
rStrToken = credential.Token.AccessToken.ToString()
rParameters.AccessToken = credential.Token.AccessToken
rParameters.RefreshToken = credential.Token.RefreshToken
Catch ex As Exception
' We encountered some kind of problem, perhaps they have not yet authenticated?
' Can we isolate that as the exception?
m_logger.Error(ex, "DoAuthentication")
Return False
End Try
Return True
End Function
然后在我的 Main
:
Dim parameters As New OAuth2Parameters
If (DoAuthentication(strToken, parameters)) Then
iResult = RESULT_SUCCESS_OAUTH
Else
Return RESULT_FAILED_OAUTH
End If
然后继续。问题是有时,即使“验证”成功,它也会开始 运行 其余代码。然后我得到一个例外:
2020-11-30 18:47:03.6762|ERROR|GoogleAuthandSync.Program|AddEventsToCalendarXML|System.IO.IOException: The process cannot access the file 'C:\Users\USERNAME\AppData\Roaming\XXXXX.Application\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user' because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
我如何验证/刷新令牌并确保我们已准备好继续以停止此异常?它并不总是发生。
(为了安全起见,我在上面的代码/错误文本中重命名了令牌文件)。
我应该使用 Await
关键字吗?
If credential.Token.IsExpired(Google.Apis.Util.SystemClock.Default) Then
Await credential.RefreshTokenAsync(CancellationToken.None)
End If
这会有什么不同吗?我注意到在这个 sample code 中他们使用 await
.
有人鼓励我在 GitHub 上询问这个问题(参见 discussion)。
事实证明,我不需要像以前那样使用 RefreshTokenAsync
。我能够简化我的代码并将其变成一个 Async
方法,例如:
Private Async Function DoAuthenticationAsync() As Task(Of Boolean)
Dim credential As UserCredential
Dim Secrets = New ClientSecrets() With {
.ClientId = m_strClientID,
.ClientSecret = m_strClientSecret
}
Try
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(Secrets, m_Scopes,
"user", CancellationToken.None,
New FileDataStore("XXXXX.Application"))
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "XXXXX"
}
m_Service = New CalendarService(initializer)
Catch ex As Exception
' We encountered some kind of problem, perhaps they have not yet authenticated?
' Can we isolate that as the exception?
m_logger.Error(ex, "DoAuthentication")
Return False
End Try
Return True
End Function
然后,我不得不将 Main
函数中的所有代码移动到一个任务中:
Private Async Function Run() As Task(Of Integer)
...
End Function
最后,我把Main
调整成这样:
Public Function Main() As Integer
Return RunAsync.Result
End Function