【问题标题】:GoogleWebAuthorizationBroker.AuthorizeAsync HangsGoogleWebAuthorizationBroker.AuthorizeAsync 挂起
【发布时间】:2015-02-18 19:34:55
【问题描述】:

我们的网站需要从后面的代码(asp.net mvc 应用程序)上传视频到youtube。我正在尝试获取 google 凭据,但是当我调用 AuthorizeAsync 时,应用程序只是挂起。我已经到处寻找解决方案,但似乎没有任何帮助。我已经在谷歌和堆栈溢出上搜索了明显的内容。我发现的大部分内容都提到该应用程序可能无法访问 appdata 文件夹,因此我尝试将文件夹更改为位于 c 驱动器、d 驱动器和实际 inetpub 位置。我测试并发现我能够让应用程序写入这些位置。

更具体地说,用户是我们的管理员,客户向我们上传视频,管理员批准。当管理员批准它们时,它会发布在我们的 youtube 帐户上。管理员无需执行任何操作,只需单击批准按钮即可。

要让这个问题成为一个实际的问题,我该怎么做才能通过 AuthorizeAsync?如果您需要更多信息,请告诉我

        UserCredential credential;
        GoogleWebAuthorizationBroker.Folder = "YouTube.Auth.Store";
        using (var stream = new FileStream(CredentialsPath, FileMode.Open,
                             FileAccess.Read))
        {
            credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.Load(stream).Secrets,
                // This OAuth 2.0 access scope allows an application to upload files to the
                // authenticated user's YouTube channel, but doesn't allow other types of access.
                new[] { YouTubeService.Scope.YoutubeUpload },
                "user",
                CancellationToken.None,
                new FileDataStore("YouTube.Auth.Store")
            ).Result;
        }

【问题讨论】:

  • 您是否尝试过使用异步,就像这里建议的那样:developers.google.com/api-client-library/dotnet/guide/…
  • 我还没有解决这个问题,但如果有人想知道,我想我在这里找到了解决方案:anilanar.wordpress.com/2013/07/19/…
  • Google 尽管是顶级互联网公司,但在为开发人员提供错误方面却很糟糕。我讨厌使用它的客户端库。
  • AuthorizeAsync 也为我挂起,经过多次试验和错误步骤后发现问题是“new FileDataStore("YouTube.Auth.Store")' 中的路径在运行它的服务器上错误。

标签: c# asp.net youtube google-api-dotnet-client


【解决方案1】:

找到了通过这个的方法。

我改用了 GoogleAuthorizationCodeFlow。结果是这样的:

        ClientSecrets secrets = new ClientSecrets()
        {
            ClientId = CLIENT_ID,
            ClientSecret = CLIENT_SECRET
        };

        var token = new TokenResponse { RefreshToken = REFRESH_TOKEN }; 
        var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
            new GoogleAuthorizationCodeFlow.Initializer 
            {
                ClientSecrets = secrets
            }), 
            "user", 
            token);

        var service = new YouTubeService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credentials,
            ApplicationName = "TestProject"
        });

【讨论】:

  • 您是直接使用字符串“user”作为 GoogleAuthorizationCodeFlow() 的参数,还是使用一些 Google 用户名?
  • 什么是REFRESH_TOKEN
  • @Rick 这就是我用于GoogleWebAuthorizationBroker.AuthorizeAsync() 通话的内容。它们看起来很相似,所以我猜你的问题的答案是“是”。
  • @Rick - 是的,您确实使用了来自 Google API 示例代码的字符串“user”:developers.google.com/youtube/v3/code_samples/…
  • @xr280xr REFRESH_TOKEN 是一个永久令牌,您以后可以用它来交换服务器端应用程序使用的访问令牌(因为这是这里的上下文)。您可以在 Google OAuth Playground developers.google.com/oauthplayground 中使用它。此处的工作流程应如下所示: (i) 您在 Google Console console.developers.google.com 中注册新项目并生成“其他”凭据。这会给你clientId和clientSecret。 (ii) 你使用GoogleWebAuthorizationBroker.AuthorizeAsync 一次授权应用程序并获得REFRESH_TOKEN (iii) 你在代码中硬编码:)
【解决方案2】:

以下代码允许您使用您的 google drive api 凭据向用户请求登录信息,并将文件下载到您的服务器以执行一些请求:

Option Strict On
Option Explicit On
Imports System.Net
Imports System.IO
Imports System.Linq
Imports System.Threading
Imports System.Web
Imports System.Web.UI.WebControls
Imports Google
Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Auth.OAuth2.Flows
Imports Google.Apis.Auth.OAuth2.Web
Imports Google.Apis.Services

'for drive
Imports Google.Apis.Drive.v2
Imports Google.Apis.Drive.v2.Data
Imports Google.Apis.Util.Store
'for AuthorizationCodeRequestUrl
Imports Google.Apis.Auth.OAuth2.Requests
Imports System.Drawing

Public Class _Default
    Inherits Page

    Private redirect_uri As String = ""
    Private client_id As String = ""
    Private client_secret As String = ""
    Private service As DriveService

    'Application logic should manage users authentication. 
    'This sample works with only one user. You can change it by retrieving
    'data from the session.
    'You use the word user because you are authorized by the user to access
    'their drive contents.
Private UserId As String = "user" 

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load

    Dim SCOPES As String() = New [String]() {"https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/drive.install"}
    Dim flow As GoogleAuthorizationCodeFlow
    'use extended class to create google authorization code flow
    'use custom own datastore
    flow = New ForceOfflineGoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer() With {
    .DataStore = New FileDataStore("D:\inetpub\wwwroot\go\filedatastore", True),
    .ClientSecrets = New ClientSecrets() With {
        .ClientId = client_id,
        .ClientSecret = client_secret
    },
    .Scopes = SCOPES
})

    Dim uri = Request.Url.ToString()
    Dim redirecturi As String = redirect_uri
    Dim code = Request("code")
    If code IsNot Nothing Then
        output.Text &= "<br />Code: " & code & "<br />"
        output.Text &= "<br />UserID: " & UserId & "<br />"
        output.Text &= "<br />URI: " & uri & "<br />"
        output.Text &= "<br />RedirectURI: " & redirecturi & "<br />"
        output.Text &= "<br />uri-substring-thing: " & uri.Substring(0, uri.IndexOf("?")) & "<br />"
        Dim token = flow.ExchangeCodeForTokenAsync(UserId, code, redirecturi, 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, redirecturi, 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.


            service = New DriveService(New BaseClientService.Initializer() With {
            .HttpClientInitializer = result.Credential,
            .ApplicationName = "Drive API Sample"
        })
        End If
    End If

End Sub

''' <summary>
''' Download a file
''' Documentation: https://developers.google.com/drive/v2/reference/files/get
''' </summary>
''' <param name="_service">a Valid authenticated DriveService</param>
''' <param name="_fileResource">File resource of the file to download</param>
''' <param name="_saveTo">location of where to save the file including the file name to save it as.</param>
''' <returns></returns>
Public Shared Function downloadFile(_service As DriveService, _fileResource As Data.File, _saveTo As String) As [Boolean]

    If Not [String].IsNullOrEmpty(_fileResource.DownloadUrl) Then
        Try
            Dim x = _service.HttpClient.GetByteArrayAsync(_fileResource.DownloadUrl)
            Dim arrBytes As Byte() = x.Result
            System.IO.File.WriteAllBytes(_saveTo, arrBytes)
            Return True
        Catch e As Exception
            Console.WriteLine("An error occurred: " + e.Message)
            Return False
        End Try
    Else
        ' The file doesn't have any content stored on Drive.
        Return False
    End If
End Function

Public Async Function GetFile(IncomingFileID As String) As Tasks.Task
    Try
        Dim request As FilesResource.GetRequest = service.Files.Get(IncomingFileID)
        Dim response As Data.File = Await request.ExecuteAsync(CancellationToken.None)
        downloadFile(service, response, "D:/inetpub/wwwroot/go/" & Guid.NewGuid.ToString)

    Catch ex As Exception
        Dim str As String = "<br />" & ex.ToString()
        str = str.Replace(Environment.NewLine, Environment.NewLine + "<br/>")
        str = str.Replace("  ", " &nbsp;")
        output.Text &= String.Format("<font color=""red"">{0}</font>", str)
    End Try
End Function


'''<summary>Gets the File Lists of the user.</summary>
Public Async Function FetchFilelists() As System.Threading.Tasks.Task
    Try
        ' Get all files of the user asynchronously.
        Dim request As FilesResource.ListRequest = service.Files.List()

        'get only my files
        request.Q = "mimeType contains 'image/'"

        Dim response As FileList = Await request.ExecuteAsync()

        ShowFilelists(response)
    Catch ex As Exception
        Dim str = ex.ToString()
        str = str.Replace(Environment.NewLine, Environment.NewLine + "<br/>")
        str = str.Replace("  ", " &nbsp;")
        output.Text = String.Format("<font color=""red"">{0}</font>", str)
    End Try
End Function

Private Sub ShowFilelists(response As FileList)
    If response.Items Is Nothing Then
        output.Text &= "You have no files!<br/>"
        Return
    End If

    output.Text &= "Showing files...<br/>"
    Dim ctr As Integer = 0
    For Each file As Google.Apis.Drive.v2.Data.File In response.Items
        ctr += 1
        Dim listPanel As New Panel() With {
            .BorderWidth = Unit.Pixel(1),
            .BorderColor = Color.Black
        }
        listPanel.Controls.Add(New Label() With {
            .Text = file.Title + "--" + file.MimeType
                })
        listPanel.Controls.Add(New Label() With {
            .Text = "<hr/>"
            })


        If file.MimeType.ToString().Contains("folder") = False Then
            listPanel.Controls.Add(New HyperLink() With {
            .Text = "Download",
            .NavigateUrl = file.WebContentLink
        })

            listPanel.Controls.Add(New Button() With {
                                   .Text = "Use This Image", .OnClientClick = "document.getElementById('currentImageId').value = '" & file.Id & "';return false;"})

        End If
        lists.Controls.Add(listPanel)
    Next

    output.Text &= "Total Files: " & ctr.ToString
End Sub



Protected Async Sub listButton_Click(sender As Object, e As EventArgs) Handles listbutton.Click
    Await FetchFilelists()
End Sub

Protected Async Sub DownloadImageButton_Click(sender As Object, e As EventArgs) Handles DownloadImageButton.Click
    Dim idToUse As String = Page.Request.Form(currentImageId.UniqueID)

    output.Text &= "<br />Attempting to download id: " & idToUse & "<br />"
    Await GetFile(idToUse)
    output.Text &= "<br />Download Completed<br />"
End Sub

'overwrite. force offline.
Friend Class ForceOfflineGoogleAuthorizationCodeFlow
    Inherits GoogleAuthorizationCodeFlow
    'source: //gotoanswer.stanford.edu/google_analytics_oauth_with_accesstype__offline_in_c-4478263/

    Public Sub New(initializer As GoogleAuthorizationCodeFlow.Initializer)
        MyBase.New(initializer)
    End Sub

    Public Overrides Function CreateAuthorizationCodeRequest(redirectUri As String) As AuthorizationCodeRequestUrl
        Dim ss = New Google.Apis.Auth.OAuth2.Requests.GoogleAuthorizationCodeRequestUrl(New Uri(AuthorizationServerUrl))
        ss.AccessType = "offline"
        ss.ApprovalPrompt = "force"
        ss.ClientId = ClientSecrets.ClientId
        ss.Scope = String.Join(" ", Scopes)
        ss.RedirectUri = redirectUri
        Return ss
    End Function
End Class


End Class

这是 WebForm .aspx 页面:

<%@ Page Title="Home Page" Language="VB" MasterPageFile="~/Site.Master"
AutoEventWireup="true" CodeBehind="Default.aspx.vb"     Inherits="PictureMyCard._Default" Async="True" %>

<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" 
runat="server">


<div>
    <asp:Button ID="listbutton" runat="server" ClientIDMode="Static" Text="list" />
<asp:Label id="output" runat="server" ClientIDMode="Static" Text="..." ></asp:Label>
</div>
    <asp:Label ID="lists" ClientIDMode="Static" Text="..." runat="server"></asp:Label>
<hr />

<asp:HiddenField ClientIDMode="Static" ID="currentImageId" runat="server" />

<asp:Button runat="server" ID="DownloadImageButton" ClientIDMode="Static"  Text="Download Image to server" />
</asp:Content>

【讨论】:

    【解决方案3】:

    如果发生这种情况,例如使用超时,则可能会中断,因为AuthorizeAsync 方法具有CancellationToken 的实现。

    • 创建TokenSource 以获取令牌本身。
    • 将 TokenSource 的 Cancellation 设置为 20 秒
    • 提取令牌以传递给AuthorizeAsync 方法。

    public async void RunAsync()
    {
        UserCredential credential;
        var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read);
    
        CancellationTokenSource cts = new CancellationTokenSource();
        cts.CancelAfter(TimeSpan.FromSeconds(20));
        CancellationToken ct = cts.Token;
    
        credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        GoogleClientSecrets.Load(stream).Secrets,
        new[] { YouTubeService.Scope.YoutubeUpload },
        "user",
        ct
        );
    
        if (ct.IsCancellationRequested) return;
    
        // do more stuff when came back authorized.
    }
    

    现在,20 秒后,当AuthorizeAsync完成时,此方法RunAsync 的调用者将收到一个异常。

    try
    {
        await RunAsync();
    }
    catch(Exception)
    {
         // "Timeout" of the RunAsync(); 
    }
    

    【讨论】:

      【解决方案4】:
      GoogleWebAuthorizationBroker.Folder = "YouTube.Auth.Store";
      

      此行会在 YouTube.Auth.Store/xxx 中生成访问令牌,您需要右键单击此文件 -> 属性 -> 复制到输出目录 -> 始终复制 在视觉工作室中

      它对我有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-04
        • 2015-11-17
        • 1970-01-01
        • 1970-01-01
        • 2015-09-21
        • 2012-12-20
        相关资源
        最近更新 更多