我能够成功连接到Google Calendar API并在我的计算机上本地运行时同步事件。但是,当Web应用程序在服务器上运行时,我收到以下错误:
No application is associated with the specified file for this operation
at Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.<AuthorizeAsync>d__1.MoveNext()
我不明白为什么它存在文件关联问题,因为我没有使用FileDataStore而是使用数据库来存储数据。在本地计算机上以调试模式使用Visual Studio时,一切正常,但在上载到生产服务器时会发生错误。
这是我用来连接API的代码:
Private Shared Function BuildService(EmployeeID As String) As Google.Apis.Calendar.v3.CalendarService
'// Google OAuth for User Calendar
Dim credential As UserCredential = GoogleWebAuthorizationBroker.AuthorizeAsync(New ClientSecrets() With { _
.ClientId = GoogleAPI.ClientID, _
.ClientSecret = GoogleAPI.ClientSecret _
}, New String() {Google.Apis.Calendar.v3.CalendarService.Scope.Calendar}, EmployeeID, CancellationToken.None, New GoogleDataStore()).Result
' Create the service.
Dim service = New Google.Apis.Calendar.v3.CalendarService(New BaseClientService.Initializer() With { _
.HttpClientInitializer = credential, _
.ApplicationName = "Influence Calandar App" _
})
Return service
End Function
这是我用来实现IDataStore的GoogleDataStore类:
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports Google.Apis.Util.Store
Imports System.Data.SqlClient
Imports System.Threading.Tasks
Imports Google.Apis.Json
Public Class GoogleDataStore
Implements IDataStore
''' <summary>Gets the full folder path.</summary>
Private Property _ConnectionExists() As [Boolean]
Get
Return m__ConnectionExists
End Get
Set(value As [Boolean])
m__ConnectionExists = Value
End Set
End Property
Private m__ConnectionExists As [Boolean]
Public ReadOnly Property connectionExists() As [Boolean]
Get
Return _ConnectionExists
End Get
End Property
''' <summary>
''' Constructs a new file data store with the specified folder. This folder is created (if it doesn't exist
''' yet) under the current directory
''' </summary>
''' <param name="folder">Folder name</param>
Public Sub New()
Dim myConnection As SqlConnection = Me.connectdb()
' Opens a connection to the database.
If _ConnectionExists Then
' check if the Table Exists;
Try
Dim myReader As SqlDataReader = Nothing
Dim myCommand As New SqlCommand("select 1 from GoogleUser where 1 = 0", myConnection)
myReader = myCommand.ExecuteReader()
While myReader.Read()
Dim hold = myReader("Column1")
End While
Catch
' table doesn't exist we create it
Dim myCommand As New SqlCommand("CREATE TABLE [dbo].[GoogleUser]( " + " [username] [nvarchar](4000) NOT NULL," + " [RefreshToken] [nvarchar](4000) NOT NULL," + " [Userid] [nvarchar](4000) NOT NULL" + " ) ON [PRIMARY]", myConnection)
myCommand.ExecuteNonQuery()
End Try
End If
myConnection.Close()
End Sub
''' <summary>
''' Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in
''' <see cref="FolderPath"/>.
''' </summary>
''' <typeparam name="T">The type to store in the data store</typeparam>
''' <param name="key">The key</param>
''' <param name="value">The value to store in the data store</param>
Public Function StoreAsync(Of T)(key As String, value As T) As Task Implements IDataStore.StoreAsync
If String.IsNullOrEmpty(key) Then
Throw New ArgumentException("Key MUST have a value")
End If
Dim serialized = NewtonsoftJsonSerializer.Instance.Serialize(value)
Dim myConnection As SqlConnection = Me.connectdb()
If Not _ConnectionExists Then
Throw New Exception("Not connected to the database")
End If
' Try and find the Row in the DB.
Using command As New SqlCommand("select Userid from GoogleUser where UserName = @username", myConnection)
command.Parameters.AddWithValue("@username", key)
Dim hold As String = Nothing
Dim myReader As SqlDataReader = command.ExecuteReader()
While myReader.Read()
hold = myReader("Userid").ToString()
End While
myReader.Close()
If hold Is Nothing Then
Try
' New User we insert it into the database
Dim insertString As String = "INSERT INTO [dbo].[GoogleUser] ([username],[RefreshToken],[Userid]) " + " VALUES (@key,@value,'1' )"
Dim commandins As New SqlCommand(insertString, myConnection)
commandins.Parameters.AddWithValue("@key", key)
commandins.Parameters.AddWithValue("@value", serialized)
commandins.ExecuteNonQuery()
Catch ex As Exception
Throw New Exception("Error inserting new row: " + ex.Message)
End Try
Else
Try
' Existing User We update it
Dim insertString As String = "update [dbo].[GoogleUser] " + " set [RefreshToken] = @value " + " where username = @key"
Dim commandins As New SqlCommand(insertString, myConnection)
commandins.Parameters.AddWithValue("@key", key)
commandins.Parameters.AddWithValue("@value", serialized)
commandins.ExecuteNonQuery()
Catch ex As Exception
Throw New Exception("Error updating user: " + ex.Message)
End Try
End If
End Using
myConnection.Close()
Return TaskEx.Delay(0)
End Function
''' <summary>
''' Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in <see cref="FolderPath"/>.
''' </summary>
''' <param name="key">The key to delete from the data store</param>
Public Function DeleteAsync(Of T)(key As String) As Task Implements IDataStore.DeleteAsync
If String.IsNullOrEmpty(key) Then
Throw New ArgumentException("Key MUST have a value")
End If
Dim myConnection As SqlConnection = Me.connectdb()
If Not _ConnectionExists Then
Throw New Exception("Not connected to the database")
End If
' Deletes the users data.
Dim deleteString As String = "delete [dbo].[GoogleUser] from " + " where username = @key"
Dim commandins As New SqlCommand(deleteString, myConnection)
commandins.Parameters.AddWithValue("@key", key)
commandins.ExecuteNonQuery()
myConnection.Close()
Return TaskEx.Delay(0)
End Function
''' <summary>
''' Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
''' in <see cref="FolderPath"/> doesn't exist.
''' </summary>
''' <typeparam name="T">The type to retrieve</typeparam>
''' <param name="key">The key to retrieve from the data store</param>
''' <returns>The stored object</returns>
Public Function GetAsync(Of T)(key As String) As Task(Of T) Implements IDataStore.GetAsync
'Key is the user string sent with AuthorizeAsync
If String.IsNullOrEmpty(key) Then
Throw New ArgumentException("Key MUST have a value")
End If
Dim tcs As New TaskCompletionSource(Of T)()
' Note: create a method for opening the connection.
Dim myConnection As SqlConnection = Me.connectdb()
' Try and find the Row in the DB.
Using command As New SqlCommand("select RefreshToken from GoogleUser where UserName = @username;", myConnection)
command.Parameters.AddWithValue("@username", key)
Dim RefreshToken As String = Nothing
Dim myReader As SqlDataReader = command.ExecuteReader()
While myReader.Read()
RefreshToken = myReader("RefreshToken").ToString()
End While
If RefreshToken Is Nothing Then
' we don't have a record so we request it of the user.
tcs.SetResult(Nothing)
Else
Try
' we have it we use that.
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize(Of T)(RefreshToken))
Catch ex As Exception
tcs.SetException(ex)
End Try
End If
End Using
Return tcs.Task
End Function
''' <summary>
''' Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
''' </summary>
Public Function ClearAsync() As Task Implements IDataStore.ClearAsync
Dim myConnection As SqlConnection = Me.connectdb()
If Not _ConnectionExists Then
Throw New Exception("Not connected to the database")
End If
' Removes all data from the Table.
Dim truncateString As String = "truncate table [dbo].[GoogleUser] "
Dim commandins As New SqlCommand(truncateString, myConnection)
commandins.ExecuteNonQuery()
myConnection.Close()
Return TaskEx.Delay(0)
End Function
''' <summary>Creates a unique stored key based on the key and the class type.</summary>
''' <param name="key">The object key</param>
''' <param name="t">The type to store or retrieve</param>
Public Shared Function GenerateStoredKey(key As String, t As Type) As String
Return String.Format("{0}-{1}", t.FullName, key)
End Function
'Handel's creating the connection to the database
Private Function connectdb() As SqlConnection
Dim myConnection As SqlConnection = Nothing
Try
myConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("db1251ConnectionString").ConnectionString)
Try
myConnection.Open()
' ensuring that we are able to make a connection to the database.
If myConnection.State = System.Data.ConnectionState.Open Then
_ConnectionExists = True
Else
Throw New ArgumentException("Error unable to open connection to the database.")
End If
Catch ex As Exception
Throw New ArgumentException("Error opening Connection to the database: " + ex.Message)
End Try
Catch ex As Exception
Throw New ArgumentException("Error creating Database Connection: " + ex.Message)
End Try
Return myConnection
End Function
End Class
任何帮助将不胜感激。我一直试图找出错误但到目前为止没有运气。由于我没有尝试访问文件,所以“没有应用程序与此操作的指定文件关联”的其他帖子都没有帮助。 “GoogleWebAuthorizationBroker”尝试打开文件,尽管被告知要使用GoogleDataStore类而不是FileDataStore,如果是这样,我该如何阻止它?
我正在运行一个托管在具有Windows Azure的VM上的vb.net Web应用程序。
答案 0 :(得分:1)
我终于可以通过从GoogleWebAuthorizationBroker切换到GoogleAuthorizationCodeFlow来进行身份验证。
希望如果您收到同样的错误,下面的代码会对您有所帮助。
Private Sub GetGoogleService()
Dim datafolder As String = Server.MapPath("App_Data/CalendarService.api.auth.store")
Dim scopes As IList(Of String) = New List(Of String)()
Dim UserId As String = aMP.currentEmployeeID
scopes.Add(Google.Apis.Calendar.v3.CalendarService.Scope.Calendar)
Dim myclientsecret As New ClientSecrets() With { _
.ClientId = GoogleAPI.ClientID, _
.ClientSecret = GoogleAPI.ClientSecret _
}
Dim flow As GoogleAuthorizationCodeFlow
flow = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer() With { _
.DataStore = New FileDataStore(datafolder), _
.ClientSecrets = myclientsecret, _
.Scopes = scopes _
})
Dim uri As String = Request.Url.ToString()
Dim code = Request("code")
If code IsNot Nothing Then
Dim token = flow.ExchangeCodeForTokenAsync(UserId, code, uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result
' Extract the right state.
Dim oauthState = AuthWebUtility.ExtracRedirectFromState(flow.DataStore, UserId, Request("state")).Result
Response.Redirect(oauthState)
Else
Dim result = New AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync(UserId, CancellationToken.None).Result
If result.RedirectUri IsNot Nothing Then
' Redirect the user to the authorization server.
Response.Redirect(result.RedirectUri)
Else
' The data store contains the user credential, so the user has been already authenticated.
'Response.Write("User Already Authorized")
Me.isConnected = New Google.Apis.Calendar.v3.CalendarService(New BaseClientService.Initializer() With { _
.HttpClientInitializer = result.Credential, _
.ApplicationName = "Calandar App" _
})
End If
End If
End Sub