这是我第一次使用Google的Drive api,所以如果我出错了,我会提前道歉。
这就是我所拥有的:
我使用网络应用程序手动浏览OAuth2舞蹈,以便在TokenResponse中获取AccessToken,RefreshToken和TokenType。这些价值都成功回归,我存储了它们。
现在,我想在Windows服务中使用相同的信息,以及我为此目的明确创建的Google帐户的相同用户名,该服务将监控该用户的云端硬盘的特定目录中的文件。
我手动创建了TokenResponse,GoogleAuthorizationCodeFlow,UserCredential和DriveService,如下所示:
//Populate the token response with the stored values
_tokenResponse = new TokenResponse
{
AccessToken = AppConfiguration.GetSetting("StoredAccessToken"),
TokenType = AppConfiguration.GetSetting("StoredTokenType"),
RefreshToken = AppConfiguration.GetSetting("StoredRefreshToken"),
Issued = DateTime.Parse(AppConfiguration.GetSetting("StoredTokenIssuedDate")),
ExpiresInSeconds = long.Parse(AppConfiguration.GetSetting("StoredExpiresInSeconds"))
};
//Next, build our code flow
_apiCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AppConfiguration.GetSetting("ApiClientId"),
ClientSecret = AppConfiguration.GetSetting("ApiClientSecret")
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore(AppConfiguration.GetSetting("RegisteredApplicationName"))
});
//Now create the UserCredential with those stored values
_credential = new UserCredential(_apiCodeFlow, AppConfiguration.GetSetting("DriveApiServiceAccountEmail"), _tokenResponse);
//And generate a DriveService instance using the UserCredential
_driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = _credential,
ApplicationName = AppConfiguration.GetSetting("RegisteredApplicationName")
});
这一切似乎都可行,但是当我尝试做类似
的事情时DriveService.Files.List.Execute()
我得到一个例外:
"Error:\"invalid_grant\", Description:\"\", Uri:\"\""
现在,我已经在谷歌文档中挖掘了好几天了,而且我几乎不知所措。我开始查看它们提供的DrEdit解决方案,但它引用了我在最新的API命名空间中看不到的对象,例如:IAuthorizationState,NativeApplicationClient。我正在使用Google.Apis.Drive.v2,Google.Apis.Auth.OAuth2,Google.Apis.Auth.OAuth2.Flows,Google.Apis.OAuth2.Responses,Google.Apis.Services,Google.Apis.Util,和Google.Apis.Util.Store名称空间。有什么我想念的吗?
我想知道它是否与请求来自的网址有任何关系,因为您必须在Google控制台中注册所有重定向网址以注册这些应用。但是,我没有看到一种方法可以指定从Windows服务执行此操作时要使用的重定向URL。这可能不是问题,只是一个想法。
我的需求相当简单 - 我只需要能够使用我保存的刷新令牌,以及仅为此目的创建的新用户的用户名,并获得一个DriveService实例,让我遍历该用户可以访问的文件。
真诚地感谢任何帮助。在此先感谢!!
使用Fiddler来监控网络流量,看起来请求正在按预期进行。 grant_type标记为“refresh_token”,所有其他项目都填充了正确的值。见附图:
在GoogleAuthorizationCodeFlow类中,有一个名为“RefreshTokenAsync”的方法,它采用用户名,刷新令牌和取消令牌。它在Fiddler中产生与调用DriveService类中的任何方法相同的结果,因此我认为它在调用DriveService时会自动尝试在幕后刷新令牌。不确定这对那些能够回答这个问题的人是否有帮助,但我想我还是会提到的。
我创建了自己的IDataStore实现,这里是GetAsync方法的代码,我已经介入了它并且它应该返回一个Token对象:
public Task<T> GetAsync<T>(string key)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
try
{
//First, load the token from our local json file
//NOTE: If using the built in FileDataStore, Google would store this same json info in the local users appdata\roaming\{appname} folder
Stream stream = new FileStream("StoredToken.json", FileMode.Open, FileAccess.Read);
//Read the file contents into a string
string sToken = (new StreamReader(stream)).ReadToEnd();
//Close the stream so that the file isn't locked
stream.Close();
stream.Dispose();
//Deserialize into the type that Google requests
var token = Google.Apis.Json.NewtonsoftJsonSerializer.Instance.Deserialize<T>(sToken);
//Set the result of the TaskCompletionSource to our value
tcs.SetResult(token);
}
catch (Exception exc)
{
//No need to throw exception here - whatever is calling this method would want to handle exceptions.
//So, just set the Exception of the TaskCompletionSource and return it
tcs.SetException(exc);
}
return tcs.Task;
}
现在,这是我更新的获取UserCredential对象并使用它来填充我的DriveService实例的方法:
_credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets: new ClientSecrets
{
ClientId = AppConfiguration.GetSetting("ApiClientId"),
ClientSecret = AppConfiguration.GetSetting("ApiClientSecret")
},
scopes: new[] {DriveService.Scope.Drive},
dataStore: new CustomTokenDataStore(),
user: AppConfiguration.GetSetting("DriveApiServiceAccountEmail"),
taskCancellationToken: CancellationToken.None).Result;
_driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = _credential,
ApplicationName = AppConfiguration.GetSetting("RegisteredApplicationName")
});
然后我尝试使用DriveService API进行简单查询:
using (GoogleDriveApiUtils util = new GoogleDriveApiUtils())
{
FilesResource.ListRequest request = util.DriveService.Files.List();
/*Documentation for using the search parameters:
* https://developers.google.com/drive/search-parameters
*/
//Add this to the query to get only folders
request.Q = "mimeType='application/vnd.google-apps.folder'";
FileList fList = request.Execute();
}
这是我得到相同异常的地方 - “invalid_grant”。从我能够研究的内容来看,这与响应的来源有关。但是,我是通过Windows服务进行此操作的 - 所以我无法知道如何将它从请求发送到DriveService的注册网址中获取。但是,我可能完全走错了路。
还有其他想法吗?再次,非常感谢所有帮助:)
答案 0 :(得分:2)
FileDataStore将信息存储在%appdata%及其获取位置。要使用存储的数据,您需要对iDataStore进行自己的限制,然后您可以发送FileDataStore的内容,这也是iDataStore的另一个限制。
这只是从我的GoogleAnalytics代码中复制而来,您可以将其更改为您的范围,一旦您创建了自己对iDatastore()的限制,它就会起作用;
IDataStore StoredRefreshToken = new myDataStore();
// Oauth2 Autentication.
using (var stream = new System.IO.FileStream("client_secret.json", System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { AnalyticsService.Scope.AnalyticsReadonly },
"user", CancellationToken.None, StoredRefreshToken).Result;
}
您可以在http://daimto.com/google-oauth2-csharp/#Loading_Stored_refresh-Token
找到它的基本示例答案 1 :(得分:1)
我弄清楚它是什么,我觉得自己像个白痴。
Back-story - 这开始是一个MVC应用程序,我在Google云端控制台中注册了应用程序,并在其中运行OAuth舞蹈。然后我被告知我们需要一个Windows服务来解析某些特定位置的一些数据,所以我为此创建了一个新用户并手动完成了OAuth舞蹈并存储了刷新令牌......但是,我完全忽略了在云控制台中注册一个新应用程序作为“已安装的应用程序”,而我的第一个应用程序是“Web应用程序”,其中存在问题。我直到今天下午才意识到我只是从网络应用程序重新使用相同的ClientId和ClientSecret值...一旦我将新应用程序注册为已安装的应用程序,然后手动浏览OAuth舞蹈以获得新的令牌,我在胜利服务中更新了我的所有参考资料,指出这些东西并完全访问了所有内容,因为我应该能够......
非常感谢DaImTo,他指出我应该使用自己的IDataStore实现 - 这是我将继续使用的最有用的信息。
如果我浪费了任何人的时间,我很抱歉,希望这有助于某人:)