Google Calendar API for UWP Windows 10 Application 出现一个或多个错误
Google Calendar API for UWP Windows 10 Application One or more errors occurred
我正在尝试使用 Google Calendar API v3,但是我在 运行 代码时遇到问题,它总是给我这样的错误:
An exception of type 'System.AggregateException' occurred in mscorlib.ni.dll but was not handled in user code
Additional information: One or more errors occurred.
我不知道为什么会这样,而且它应该也能正常工作。这是它的屏幕截图:
我的代码也是:
UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new Uri("ms-appx:///Assets/client_secrets.json"),
Scopes,
"user",
CancellationToken.None).Result;
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var calendarService = new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "Windows 10 Calendar sample"
});
var calendarListResource = await calendarService.CalendarList.List().ExecuteAsync();
如果您至少可以帮助通过 REST API 调用它,那也很好,但您必须考虑到它是 UWP,因此它还有另一种方法可以让它工作。
因为我已经尝试通过 REST API,但我总是得到 "Request error code 400".
感谢您的关注。
Google API .NET 客户端库目前不支持 UWP。所以我们不能使用 Google.Apis.Calendar.v3 Client Library in UWP apps now. For more info, please see the similar question: .
要在 UWP 中使用 Google 日历 API,我们可以通过 REST API 调用它。要使用 REST API,我们需要先授权请求。如何授权请求,请见Authorizing Requests to the Google Calendar API and Using OAuth 2.0 for Mobile and Desktop Applications.
获得访问令牌后,我们可以调用日历 API,如下所示:
var clientId = "{Your Client Id}";
var redirectURI = "pw.oauth2:/oauth2redirect";
var scope = "https://www.googleapis.com/auth/calendar.readonly";
var SpotifyUrl = $"https://accounts.google.com/o/oauth2/auth?client_id={clientId}&redirect_uri={Uri.EscapeDataString(redirectURI)}&response_type=code&scope={Uri.EscapeDataString(scope)}";
var StartUri = new Uri(SpotifyUrl);
var EndUri = new Uri(redirectURI);
// Get Authorization code
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var decoder = new WwwFormUrlDecoder(new Uri(WebAuthenticationResult.ResponseData).Query);
if (decoder[0].Name != "code")
{
System.Diagnostics.Debug.WriteLine($"OAuth authorization error: {decoder.GetFirstValueByName("error")}.");
return;
}
var autorizationCode = decoder.GetFirstValueByName("code");
//Get Access Token
var pairs = new Dictionary<string, string>();
pairs.Add("code", autorizationCode);
pairs.Add("client_id", clientId);
pairs.Add("redirect_uri", redirectURI);
pairs.Add("grant_type", "authorization_code");
var formContent = new Windows.Web.Http.HttpFormUrlEncodedContent(pairs);
var client = new Windows.Web.Http.HttpClient();
var httpResponseMessage = await client.PostAsync(new Uri("https://www.googleapis.com/oauth2/v4/token"), formContent);
if (!httpResponseMessage.IsSuccessStatusCode)
{
System.Diagnostics.Debug.WriteLine($"OAuth authorization error: {httpResponseMessage.StatusCode}.");
return;
}
string jsonString = await httpResponseMessage.Content.ReadAsStringAsync();
var jsonObject = Windows.Data.Json.JsonObject.Parse(jsonString);
var accessToken = jsonObject["access_token"].GetString();
//Call Google Calendar API
using (var httpRequest = new Windows.Web.Http.HttpRequestMessage())
{
string calendarAPI = "https://www.googleapis.com/calendar/v3/users/me/calendarList";
httpRequest.Method = Windows.Web.Http.HttpMethod.Get;
httpRequest.RequestUri = new Uri(calendarAPI);
httpRequest.Headers.Authorization = new Windows.Web.Http.Headers.HttpCredentialsHeaderValue("Bearer", accessToken);
var response = await client.SendRequestAsync(httpRequest);
if (response.IsSuccessStatusCode)
{
var listString = await response.Content.ReadAsStringAsync();
//TODO
}
}
}
我的 UWP 应用程序中有 Google .NET 客户端。诀窍是您必须将它放入 .NET Standard 2.0 Class 库中,公开您需要的 API 服务,然后从您的 UWP 应用程序引用该库。
此外,您必须自己处理获取身份验证令牌的过程。这并没有那么多工作,Drive APIs 和 Calendar APIs工作得很好(我唯一尝试过的)。您可以看到我将包含身份验证令牌和其他身份验证详细信息的简单 class 传递给名为 Initialize
.
的方法
这是我在 .NET Standard 2.0 class 库中使用的单个 class:
namespace GoogleProxy
{
public class GoogleService
{
public CalendarService calendarService { get; private set; }
public DriveService driveService { get; private set; }
public GoogleService()
{
}
public void Initialize(AuthResult authResult)
{
var credential = GetCredentialForApi(authResult);
var baseInitializer = new BaseClientService.Initializer { HttpClientInitializer = credential, ApplicationName = "{your app name here}" };
calendarService = new Google.Apis.Calendar.v3.CalendarService(baseInitializer);
driveService = new Google.Apis.Drive.v3.DriveService(baseInitializer);
}
private UserCredential GetCredentialForApi(AuthResult authResult)
{
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "{your app client id here}",
ClientSecret = "",
},
Scopes = new string[] { "openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/calendar.events.readonly", "https://www.googleapis.com/auth/drive.readonly" },
};
var flow = new GoogleAuthorizationCodeFlow(initializer);
var token = new TokenResponse()
{
AccessToken = authResult.AccessToken,
RefreshToken = authResult.RefreshToken,
ExpiresInSeconds = authResult.ExpirationInSeconds,
IdToken = authResult.IdToken,
IssuedUtc = authResult.IssueDateTime,
Scope = "openid email profile https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events.readonly https://www.googleapis.com/auth/drive.readonly",
TokenType = "bearer" };
return new UserCredential(flow, authResult.Id, token);
}
}
}
为了从 google 获取身份验证令牌,您必须使用自定义方案。在 google 服务控制台上将您的应用程序注册为 'iOS' 应用程序并输入 URI 方案(唯一的东西)。然后将此方案添加到 Declarations->Protocol 下的 UWP 清单中。在您的 App.xaml.cs:
中处理
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
if (args.Kind == ActivationKind.Protocol)
{
ProtocolActivatedEventArgs protocolArgs = (ProtocolActivatedEventArgs)args;
Uri uri = protocolArgs.Uri;
Debug.WriteLine("Authorization Response: " + uri.AbsoluteUri);
locator.AccountsService.GoogleExternalAuthWait.Set(uri.Query);
}
}
GoogleExternalAuthWait
来自我发现的一些关于如何创建异步 ManualResetEvent 的神奇代码。 https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-1-asyncmanualresetevent/ 看起来像这样(我只是把它转成泛型)
public class AsyncManualResetEvent<T>
{
private volatile TaskCompletionSource<T> m_tcs = new TaskCompletionSource<T>();
public Task<T> WaitAsync() { return m_tcs.Task; }
public void Set(T TResult) { m_tcs.TrySetResult(TResult); }
public bool IsReset => !m_tcs.Task.IsCompleted;
public void Reset()
{
while (true)
{
var tcs = m_tcs;
if (!tcs.Task.IsCompleted ||
Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource<T>(), tcs) == tcs)
return;
}
}
}
这就是您启动 Google 授权的方式。发生的事情是它启动外部浏览器以开始 google 签名过程,然后等待(这就是 AsyncManualResetEvent
所做的)。完成后,Google 将使用您的自定义方案启动 URI。您应该会收到一个消息对话框,说明浏览器正在尝试打开应用程序...单击确定,AsyncManualResetEvent
继续并完成身份验证过程。您需要制作一个 class,其中包含要传递给您的 class 图书馆的所有身份验证信息。
private async Task<AuthResult> AuthenticateGoogleAsync()
{
try
{
var stateGuid = Guid.NewGuid().ToString();
var expiration = DateTimeOffset.Now;
var url = $"{GoogleAuthorizationEndpoint}?client_id={WebUtility.UrlEncode(GoogleAccountClientId)}&redirect_uri={WebUtility.UrlEncode(GoogleRedirectURI)}&state={stateGuid}&scope={WebUtility.UrlEncode(GoogleScopes)}&display=popup&response_type=code";
var success = Windows.System.Launcher.LaunchUriAsync(new Uri(url));
GoogleExternalAuthWait = new AsyncManualResetEvent<string>();
var query = await GoogleExternalAuthWait.WaitAsync();
var dictionary = query.Substring(1).Split('&').ToDictionary(x => x.Split('=')[0], x => Uri.UnescapeDataString(x.Split('=')[1]));
if (dictionary.ContainsKey("error"))
{
return null;
}
if (!dictionary.ContainsKey("code") || !dictionary.ContainsKey("state"))
{
return null;
}
if (dictionary["state"] != stateGuid)
return null;
string tokenRequestBody = $"code={dictionary["code"]}&redirect_uri={Uri.EscapeDataString(GoogleRedirectURI)}&client_id={GoogleAccountClientId}&access_type=offline&scope=&grant_type=authorization_code";
StringContent content = new StringContent(tokenRequestBody, Encoding.UTF8, "application/x-www-form-urlencoded");
// Performs the authorization code exchange.
using (HttpClientHandler handler = new HttpClientHandler())
{
handler.AllowAutoRedirect = true;
using (HttpClient client = new HttpClient(handler))
{
HttpResponseMessage response = await client.PostAsync(GoogleTokenEndpoint, content);
if (response.IsSuccessStatusCode)
{
var stringResponse = await response.Content.ReadAsStringAsync();
var json = JObject.Parse(stringResponse);
var id = DecodeIdFromJWT((string)json["id_token"]);
var oauthToken = new AuthResult()
{
Provider = AccountType.Google,
AccessToken = (string)json["access_token"],
Expiration = DateTimeOffset.Now + TimeSpan.FromSeconds(int.Parse((string)json["expires_in"])),
Id = id,
IdToken = (string)json["id_token"],
ExpirationInSeconds = long.Parse((string)json["expires_in"]),
IssueDateTime = DateTime.Now,
RefreshToken = (string)json["refresh_token"]
};
return oauthToken;
}
else
{
return null;
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
我正在尝试使用 Google Calendar API v3,但是我在 运行 代码时遇到问题,它总是给我这样的错误:
An exception of type 'System.AggregateException' occurred in mscorlib.ni.dll but was not handled in user code Additional information: One or more errors occurred.
我不知道为什么会这样,而且它应该也能正常工作。这是它的屏幕截图:
我的代码也是:
UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new Uri("ms-appx:///Assets/client_secrets.json"),
Scopes,
"user",
CancellationToken.None).Result;
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var calendarService = new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "Windows 10 Calendar sample"
});
var calendarListResource = await calendarService.CalendarList.List().ExecuteAsync();
如果您至少可以帮助通过 REST API 调用它,那也很好,但您必须考虑到它是 UWP,因此它还有另一种方法可以让它工作。 因为我已经尝试通过 REST API,但我总是得到 "Request error code 400".
感谢您的关注。
Google API .NET 客户端库目前不支持 UWP。所以我们不能使用 Google.Apis.Calendar.v3 Client Library in UWP apps now. For more info, please see the similar question:
要在 UWP 中使用 Google 日历 API,我们可以通过 REST API 调用它。要使用 REST API,我们需要先授权请求。如何授权请求,请见Authorizing Requests to the Google Calendar API and Using OAuth 2.0 for Mobile and Desktop Applications.
获得访问令牌后,我们可以调用日历 API,如下所示:
var clientId = "{Your Client Id}";
var redirectURI = "pw.oauth2:/oauth2redirect";
var scope = "https://www.googleapis.com/auth/calendar.readonly";
var SpotifyUrl = $"https://accounts.google.com/o/oauth2/auth?client_id={clientId}&redirect_uri={Uri.EscapeDataString(redirectURI)}&response_type=code&scope={Uri.EscapeDataString(scope)}";
var StartUri = new Uri(SpotifyUrl);
var EndUri = new Uri(redirectURI);
// Get Authorization code
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var decoder = new WwwFormUrlDecoder(new Uri(WebAuthenticationResult.ResponseData).Query);
if (decoder[0].Name != "code")
{
System.Diagnostics.Debug.WriteLine($"OAuth authorization error: {decoder.GetFirstValueByName("error")}.");
return;
}
var autorizationCode = decoder.GetFirstValueByName("code");
//Get Access Token
var pairs = new Dictionary<string, string>();
pairs.Add("code", autorizationCode);
pairs.Add("client_id", clientId);
pairs.Add("redirect_uri", redirectURI);
pairs.Add("grant_type", "authorization_code");
var formContent = new Windows.Web.Http.HttpFormUrlEncodedContent(pairs);
var client = new Windows.Web.Http.HttpClient();
var httpResponseMessage = await client.PostAsync(new Uri("https://www.googleapis.com/oauth2/v4/token"), formContent);
if (!httpResponseMessage.IsSuccessStatusCode)
{
System.Diagnostics.Debug.WriteLine($"OAuth authorization error: {httpResponseMessage.StatusCode}.");
return;
}
string jsonString = await httpResponseMessage.Content.ReadAsStringAsync();
var jsonObject = Windows.Data.Json.JsonObject.Parse(jsonString);
var accessToken = jsonObject["access_token"].GetString();
//Call Google Calendar API
using (var httpRequest = new Windows.Web.Http.HttpRequestMessage())
{
string calendarAPI = "https://www.googleapis.com/calendar/v3/users/me/calendarList";
httpRequest.Method = Windows.Web.Http.HttpMethod.Get;
httpRequest.RequestUri = new Uri(calendarAPI);
httpRequest.Headers.Authorization = new Windows.Web.Http.Headers.HttpCredentialsHeaderValue("Bearer", accessToken);
var response = await client.SendRequestAsync(httpRequest);
if (response.IsSuccessStatusCode)
{
var listString = await response.Content.ReadAsStringAsync();
//TODO
}
}
}
我的 UWP 应用程序中有 Google .NET 客户端。诀窍是您必须将它放入 .NET Standard 2.0 Class 库中,公开您需要的 API 服务,然后从您的 UWP 应用程序引用该库。
此外,您必须自己处理获取身份验证令牌的过程。这并没有那么多工作,Drive APIs 和 Calendar APIs工作得很好(我唯一尝试过的)。您可以看到我将包含身份验证令牌和其他身份验证详细信息的简单 class 传递给名为 Initialize
.
这是我在 .NET Standard 2.0 class 库中使用的单个 class:
namespace GoogleProxy
{
public class GoogleService
{
public CalendarService calendarService { get; private set; }
public DriveService driveService { get; private set; }
public GoogleService()
{
}
public void Initialize(AuthResult authResult)
{
var credential = GetCredentialForApi(authResult);
var baseInitializer = new BaseClientService.Initializer { HttpClientInitializer = credential, ApplicationName = "{your app name here}" };
calendarService = new Google.Apis.Calendar.v3.CalendarService(baseInitializer);
driveService = new Google.Apis.Drive.v3.DriveService(baseInitializer);
}
private UserCredential GetCredentialForApi(AuthResult authResult)
{
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "{your app client id here}",
ClientSecret = "",
},
Scopes = new string[] { "openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/calendar.events.readonly", "https://www.googleapis.com/auth/drive.readonly" },
};
var flow = new GoogleAuthorizationCodeFlow(initializer);
var token = new TokenResponse()
{
AccessToken = authResult.AccessToken,
RefreshToken = authResult.RefreshToken,
ExpiresInSeconds = authResult.ExpirationInSeconds,
IdToken = authResult.IdToken,
IssuedUtc = authResult.IssueDateTime,
Scope = "openid email profile https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events.readonly https://www.googleapis.com/auth/drive.readonly",
TokenType = "bearer" };
return new UserCredential(flow, authResult.Id, token);
}
}
}
为了从 google 获取身份验证令牌,您必须使用自定义方案。在 google 服务控制台上将您的应用程序注册为 'iOS' 应用程序并输入 URI 方案(唯一的东西)。然后将此方案添加到 Declarations->Protocol 下的 UWP 清单中。在您的 App.xaml.cs:
中处理protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
if (args.Kind == ActivationKind.Protocol)
{
ProtocolActivatedEventArgs protocolArgs = (ProtocolActivatedEventArgs)args;
Uri uri = protocolArgs.Uri;
Debug.WriteLine("Authorization Response: " + uri.AbsoluteUri);
locator.AccountsService.GoogleExternalAuthWait.Set(uri.Query);
}
}
GoogleExternalAuthWait
来自我发现的一些关于如何创建异步 ManualResetEvent 的神奇代码。 https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-1-asyncmanualresetevent/ 看起来像这样(我只是把它转成泛型)
public class AsyncManualResetEvent<T>
{
private volatile TaskCompletionSource<T> m_tcs = new TaskCompletionSource<T>();
public Task<T> WaitAsync() { return m_tcs.Task; }
public void Set(T TResult) { m_tcs.TrySetResult(TResult); }
public bool IsReset => !m_tcs.Task.IsCompleted;
public void Reset()
{
while (true)
{
var tcs = m_tcs;
if (!tcs.Task.IsCompleted ||
Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource<T>(), tcs) == tcs)
return;
}
}
}
这就是您启动 Google 授权的方式。发生的事情是它启动外部浏览器以开始 google 签名过程,然后等待(这就是 AsyncManualResetEvent
所做的)。完成后,Google 将使用您的自定义方案启动 URI。您应该会收到一个消息对话框,说明浏览器正在尝试打开应用程序...单击确定,AsyncManualResetEvent
继续并完成身份验证过程。您需要制作一个 class,其中包含要传递给您的 class 图书馆的所有身份验证信息。
private async Task<AuthResult> AuthenticateGoogleAsync()
{
try
{
var stateGuid = Guid.NewGuid().ToString();
var expiration = DateTimeOffset.Now;
var url = $"{GoogleAuthorizationEndpoint}?client_id={WebUtility.UrlEncode(GoogleAccountClientId)}&redirect_uri={WebUtility.UrlEncode(GoogleRedirectURI)}&state={stateGuid}&scope={WebUtility.UrlEncode(GoogleScopes)}&display=popup&response_type=code";
var success = Windows.System.Launcher.LaunchUriAsync(new Uri(url));
GoogleExternalAuthWait = new AsyncManualResetEvent<string>();
var query = await GoogleExternalAuthWait.WaitAsync();
var dictionary = query.Substring(1).Split('&').ToDictionary(x => x.Split('=')[0], x => Uri.UnescapeDataString(x.Split('=')[1]));
if (dictionary.ContainsKey("error"))
{
return null;
}
if (!dictionary.ContainsKey("code") || !dictionary.ContainsKey("state"))
{
return null;
}
if (dictionary["state"] != stateGuid)
return null;
string tokenRequestBody = $"code={dictionary["code"]}&redirect_uri={Uri.EscapeDataString(GoogleRedirectURI)}&client_id={GoogleAccountClientId}&access_type=offline&scope=&grant_type=authorization_code";
StringContent content = new StringContent(tokenRequestBody, Encoding.UTF8, "application/x-www-form-urlencoded");
// Performs the authorization code exchange.
using (HttpClientHandler handler = new HttpClientHandler())
{
handler.AllowAutoRedirect = true;
using (HttpClient client = new HttpClient(handler))
{
HttpResponseMessage response = await client.PostAsync(GoogleTokenEndpoint, content);
if (response.IsSuccessStatusCode)
{
var stringResponse = await response.Content.ReadAsStringAsync();
var json = JObject.Parse(stringResponse);
var id = DecodeIdFromJWT((string)json["id_token"]);
var oauthToken = new AuthResult()
{
Provider = AccountType.Google,
AccessToken = (string)json["access_token"],
Expiration = DateTimeOffset.Now + TimeSpan.FromSeconds(int.Parse((string)json["expires_in"])),
Id = id,
IdToken = (string)json["id_token"],
ExpirationInSeconds = long.Parse((string)json["expires_in"]),
IssueDateTime = DateTime.Now,
RefreshToken = (string)json["refresh_token"]
};
return oauthToken;
}
else
{
return null;
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}