如何使用服务帐户通过.NET C#访问Google AnalyticsAPI V3?

时间:2022-04-06 14:18:49

I realized this question has been previously asked but with little in the way of example code, so I am asking again but with at least a little bit of direction.

我意识到这个问题之前已经被问过,但是代码示例的方式很少,所以我再问一次,但至少有一点方向。

After hours of searching, I have come up with the following partial implementation.

经过几个小时的搜索,我得出了以下部分实现。

namespace GoogleAnalyticsAPITest.Console
{
    using System.Security.Cryptography.X509Certificates;
    using DotNetOpenAuth.OAuth2;
    using Google.Apis.Analytics.v3;
    using Google.Apis.Analytics.v3.Data;
    using Google.Apis.Authentication.OAuth2;
    using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;

    class Program
    {
        static void Main(string[] args)
        {
            log4net.Config.XmlConfigurator.Configure();
            string Scope = Google.Apis.Analytics.v3.AnalyticsService.Scopes.Analytics.ToString().ToLower();
            string scopeUrl = "https://www.googleapis.com/auth/" + Scope;
            const string ServiceAccountId = "nnnnnnnnnnn.apps.googleusercontent.com";
            const string ServiceAccountUser = "nnnnnnnnnnn@developer.gserviceaccount.com";
            AssertionFlowClient client = new AssertionFlowClient(
                GoogleAuthenticationServer.Description, new X509Certificate2(@"7039572692013fc5deada350904f55bad2588a2a-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable))
            {
                Scope = scopeUrl,
                ServiceAccountId = ServiceAccountId//,ServiceAccountUser = ServiceAccountUser
            };
            IAuthorizationState state = AssertionFlowClient.GetState(client);
            AnalyticsService service = new AnalyticsService(authenticator);
            string profileId = "ga:xxxxxxxx";
            string startDate = "2010-10-01";
            string endDate = "2010-10-18";
            string metrics = "ga:visits";
            DataResource.GaResource.GetRequest request = service.Data.Ga.Get(profileId, startDate, endDate, metrics);
            request.Dimensions = "ga:date";
            GaData data = request.Fetch();
        }
    }
}

I have a couple issues. The call to AssertionFlowClient.GetState(client) results in a "invalid_scope" response as seen in the DotNetOpenAuth log of

我有几个问题。对AssertionFlowClient.GetState(客户端)的调用会导致“invalid_scope”响应,如DotNetOpenAuth日志中所示。

2012-10-19 13:27:36,272 (GMT-4) [8] INFO DotNetOpenAuth - DotNetOpenAuth, Version=4.0.0.11165, Culture=neutral, PublicKeyToken=2780ccd10d57b246 (official) 2012-10-19 13:27:36,284 (GMT-4) [8] DEBUG DotNetOpenAuth.Messaging.Channel - Preparing to send AssertionFlowMessage (2.0) message. 2012-10-19 13:27:36,294 (GMT-4) [8] INFO DotNetOpenAuth.Messaging.Channel - Prepared outgoing AssertionFlowMessage (2.0) message for https://accounts.google.com/o/oauth2/token: grant_type: assertion assertion_type: http://oauth.net/grant_type/jwt/1.0/bearer assertion: (a bunch of encoded characters go here)

2012-10-19 13:27:36,272(GMT-4)[8] INFO DotNetOpenAuth - DotNetOpenAuth,Version = 4.0.0.11165,Culture = neutral,PublicKeyToken = 2780ccd10d57b246(official)2012-10-19 13:27:36,284( GMT-4)[8] DEBUG DotNetOpenAuth.Messaging.Channel - 准备发送AssertionFlowMessage(2.0)消息。 2012-10-19 13:27:36,294(GMT-4)[8] INFO DotNetOpenAuth.Messaging.Channel - 为https://accounts.google.com/o/oauth2/token准备的传出AssertionFlowMessage(2.0)消息:grant_type :assertion assertion_type:http://oauth.net/grant_type/jwt/1.0/bearer assertion :(一堆编码字符在这里)

2012-10-19 13:27:36,296 (GMT-4) [8] DEBUG DotNetOpenAuth.Messaging.Channel - Sending AssertionFlowMessage request. 2012-10-19 13:27:36,830 (GMT-4) [8] DEBUG DotNetOpenAuth.Http - HTTP POST https://accounts.google.com/o/oauth2/token 2012-10-19 13:27:36,954 (GMT-4) [8] ERROR DotNetOpenAuth.Http - WebException from https://accounts.google.com/o/oauth2/token: { "error" : "invalid_scope" }

2012-10-19 13:27:36,296(GMT-4)[8] DEBUG DotNetOpenAuth.Messaging.Channel - 发送AssertionFlowMessage请求。 2012-10-19 13:27:36,830(GMT-4)[8] DEBUG DotNetOpenAuth.Http - HTTP POST https://accounts.google.com/o/oauth2/token 2012-10-19 13:27:36,954 (GMT-4)[8]错误DotNetOpenAuth.Http - 来自https://accounts.google.com/o/oauth2/token的WebException:{“error”:“invalid_scope”}

I have tried specifying one or both of ServiceAccountId and ServiceAccountUser with no luck.

我已经尝试过指定一个或两个ServiceAccountId和ServiceAccountUser没有运气。

Second, even if I get an IAuthorizationState, I am not sure how I get an IAuthenticator that can be passed to the AnalyticsService constructor.

其次,即使我得到IAuthorizationState,我也不确定如何获得可以传递给AnalyticsService构造函数的IAuthenticator。

The following is the web.config I use to enable DotNetOpenAuth logging.

以下是我用来启用DotNetOpenAuth日志记录的web.config。

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=1.2.10.0, Culture=neutral, publicKeyToken=1b44e1d426115821" />
    <!--<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler" requirePermission="false"/>-->
    <sectionGroup name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection, DotNetOpenAuth">
      <section name="openid" type="DotNetOpenAuth.Configuration.OpenIdElement, DotNetOpenAuth" requirePermission="false" allowLocation="true"/>
      <section name="oauth" type="DotNetOpenAuth.Configuration.OAuthElement, DotNetOpenAuth" requirePermission="false" allowLocation="true"/>
      <section name="messaging" type="DotNetOpenAuth.Configuration.MessagingElement, DotNetOpenAuth" requirePermission="false" allowLocation="true"/>
      <section name="reporting" type="DotNetOpenAuth.Configuration.ReportingElement, DotNetOpenAuth" requirePermission="false" allowLocation="true"/>
    </sectionGroup>
  </configSections>
  <log4net>
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <file value="DotNetOpenAuth.log"/>
      <appendToFile value="true"/>
      <rollingStyle value="Size"/>
      <maxSizeRollBackups value="10"/>
      <maximumFileSize value="100KB"/>
      <staticLogFileName value="true"/>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date (GMT%date{%z}) [%thread] %-5level %logger - %message%newline"/>
      </layout>
    </appender>
    <appender name="TracePageAppender" type="OpenIdProviderWebForms.Code.TracePageAppender, OpenIdProviderWebForms">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date (GMT%date{%z}) [%thread] %-5level %logger - %message%newline"/>
      </layout>
    </appender>
    <!-- Setup the root category, add the appenders and set the default level -->
    <root>
      <level value="ALL"/>
      <appender-ref ref="RollingFileAppender"/>
      <appender-ref ref="TracePageAppender"/>
    </root>
    <!-- Specify the level for some specific categories -->
    <logger name="DotNetOpenAuth">
      <level value="ALL"/>
    </logger>
  </log4net>
  <dotNetOpenAuth>
    <!-- This is an optional configuration section where aspects of dotnetopenauth can be customized. -->
    <!-- For a complete set of configuration options see http://www.dotnetopenauth.net/developers/code-snippets/configuration-options/ -->
    <!--<messaging clockSkew="00:10:00" lifetime="00:03:00" strict="true">-->
    <!--<messaging>
      <untrustedWebRequest timeout="00:00:30" readWriteTimeout="00:00:01.500" maximumBytesToRead="1048576" maximumRedirections="10">
        <whitelistHosts>
          -->
    <!-- Uncomment to enable communication with localhost (should generally not activate in production!) -->
    <!--
          <add name="localhost"/>            
        </whitelistHosts>
      </untrustedWebRequest>
    </messaging>-->
    <!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. -->
    <reporting enabled="false"/>
  </dotNetOpenAuth>
  <appSettings>
    <!--<add key="log4net.Internal.Debug" value="true" />-->
  </appSettings>
  <runtime>
  </runtime>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

5 个解决方案

#1


11  

The following code, corrected from my original question, is based on the example provided by Ian Fraser at:

以下代码,根据我原来的问题进行了更正,基于Ian Fraser提供的示例:

https://groups.google.com/forum/#!msg/google-search-api-for-shopping/4uUGirzH4Rw/__c0e4hj0ekJ

His code addressed three issues:

他的代码解决了三个问题:

  1. It appears as though AnalyticsService.Scopes.AnalyticsReadonly does not work, at least not for me or the way I am doing it.
  2. 似乎AnalyticsService.Scopes.AnalyticsReadonly不起作用,至少不适合我或我的方式。

  3. For some reason, the ServiceAccountUser must be assigned to the ServiceAccountId property of the AssertionFlowClient instance.
  4. 出于某种原因,必须将ServiceAccountUser分配给AssertionFlowClient实例的ServiceAccountId属性。

  5. OAuth2Authenticator provides the IAuthenticator that I was looking for.
  6. OAuth2Authenticator提供了我正在寻找的IAuthenticator。

In your project, include references to:

在您的项目中,包括以下内容的引用

  • Lib\DotNetOpenAuth.dll
  • Lib\Google.Apis.dll
  • Lib\Google.Apis.Authentication.OAuth2.dll
  • Services\AnalyticsService\Google.Apis.Analytics.v3.dll

-

namespace GoogleAnalyticsAPITest.Console
{
    using System.Security.Cryptography.X509Certificates;
    using Google.Apis.Analytics.v3;
    using Google.Apis.Analytics.v3.Data;
    using Google.Apis.Authentication.OAuth2;
    using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
    using Google.Apis.Util;

    class Program
    {
        static void Main(string[] args)
        {
            log4net.Config.XmlConfigurator.Configure();            
            const string ServiceAccountId = "nnnnnnnnnnn.apps.googleusercontent.com";
            const string ServiceAccountUser = "nnnnnnnnnnn@developer.gserviceaccount.com";
            AssertionFlowClient client = new AssertionFlowClient(
                GoogleAuthenticationServer.Description, new X509Certificate2(@"value-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable))
            {
                Scope = AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue(),
                ServiceAccountId = ServiceAccountUser //Bug, why does ServiceAccountUser have to be assigned to ServiceAccountId
                //,ServiceAccountUser = ServiceAccountUser
            };
            OAuth2Authenticator<AssertionFlowClient> authenticator = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);            
            AnalyticsService service = new AnalyticsService(authenticator);            
            string profileId = "ga:64968920";
            string startDate = "2010-10-01";
            string endDate = "2010-10-31";
            string metrics = "ga:visits";
            DataResource.GaResource.GetRequest request = service.Data.Ga.Get(profileId, startDate, endDate, metrics);
            request.Dimensions = "ga:date";
            GaData data = request.Fetch();            
        }

    }
}

#2


4  

I was checking out the analytics API yesterday and noticed how undocumented it is and no samples etc.

我昨天检查了分析API,并注意到它没有文档,没有样本等。

Any ways, I have created a library that you could use to access analytics easily with couple of lines and make direct databinding to DataTables for data returned it's open source on the github so feel free to contribute :)

无论如何,我已经创建了一个库,您可以使用它来轻松访问分析,并使用几行来直接数据绑定到DataTables,并在github上返回它的开源,所以随时可以贡献:)

https://github.com/rmostafa/DotNetAnalyticsAPI

Usage

Analytics.AnalyticsManager manager = new Analytics.AnalyticsManager(Server.MapPath("~/bin/privatekey.p12"), "YOUR_EMAIL");
            manager.LoadAnalyticsProfiles();


List<Analytics.Data.DataItem> metrics = new List<Analytics.Data.DataItem>();
metrics.Add(Analytics.Data.Visitor.Metrics.visitors);
metrics.Add(Analytics.Data.Session.Metrics.visits);
List<Analytics.Data.DataItem> dimensions = new List<Analytics.Data.DataItem>();
dimensions.Add(Analytics.Data.GeoNetwork.Dimensions.country);
dimensions.Add(Analytics.Data.GeoNetwork.Dimensions.city);


System.Data.DataTable table = manager.GetGaDataTable(DateTime.Today.AddDays(-3), DateTime.Today, metrics, dimensions, null, metrics);

There is direct code mapping for All Google API Reporting commands categorized same way like the API so you could it even without reading the API Documentation at all since all features there are documented in the attributes, I have wrote code that parsed the complete api documentation and resourced the Metrics, Dimensions, Calculated Features in an XML that i generated from physical classes that you could use directly like the example above it's fun to play with :) enjoy

所有Google API报告命令的直接代码映射都按照与API类似的方式进行分类,因此即使没有阅读API文档也可以,因为属性中记录了所有功能,我编写的代码解析了完整的api文档和资源中我使用物理类生成的XML中的度量标准,维度,计算特征,您可以直接使用它们,就像上面的示例一样,玩起来很有趣:)享受

https://github.com/rmostafa/DotNetAnalyticsAPI

#3


3  

string scope = Google.Apis.Util.Utilities.GetStringValue(AnalyticsService.Scopes.AnalyticsReadonly);

#4


2  

Here is my working example posted here [1]: Automated use of google-api-dotnet-client with OAuth 2.0 I put a lot the research into finding and piecing the code together hope this saves you some time.

这是我在这里发布的工作示例[1]:使用OAuth 2.0自动使用google-api-dotnet-client我将大量研究工作放在一起寻找并拼凑代码希望这可以节省您一些时间。

#5


1  

Regarding Richard Collette answer, be careful that using his method if you want to use Analytics API in READONLY mode, the correct way to use it is:

关于Richard Collette的回答,如果你想在READONLY模式下使用Analytics API,请注意使用他的方法,使用它的正确方法是:

string Scope = "analytics.readonly"

and not

string Scope = AnalyticsService.Scopes.AnalyticsReadOnly.ToString().ToLower()

as he seems to tell that there is a bug. In fact, the bug is that the .toString() method returns analyticsreadonly and NOT analytics.readonly which is the way that Google likes. That's it.

因为他似乎告诉我有一个错误。事实上,错误是.toString()方法返回analyticsreadonly而不是analytics.readonly这是Google喜欢的方式。而已。

#1


11  

The following code, corrected from my original question, is based on the example provided by Ian Fraser at:

以下代码,根据我原来的问题进行了更正,基于Ian Fraser提供的示例:

https://groups.google.com/forum/#!msg/google-search-api-for-shopping/4uUGirzH4Rw/__c0e4hj0ekJ

His code addressed three issues:

他的代码解决了三个问题:

  1. It appears as though AnalyticsService.Scopes.AnalyticsReadonly does not work, at least not for me or the way I am doing it.
  2. 似乎AnalyticsService.Scopes.AnalyticsReadonly不起作用,至少不适合我或我的方式。

  3. For some reason, the ServiceAccountUser must be assigned to the ServiceAccountId property of the AssertionFlowClient instance.
  4. 出于某种原因,必须将ServiceAccountUser分配给AssertionFlowClient实例的ServiceAccountId属性。

  5. OAuth2Authenticator provides the IAuthenticator that I was looking for.
  6. OAuth2Authenticator提供了我正在寻找的IAuthenticator。

In your project, include references to:

在您的项目中,包括以下内容的引用

  • Lib\DotNetOpenAuth.dll
  • Lib\Google.Apis.dll
  • Lib\Google.Apis.Authentication.OAuth2.dll
  • Services\AnalyticsService\Google.Apis.Analytics.v3.dll

-

namespace GoogleAnalyticsAPITest.Console
{
    using System.Security.Cryptography.X509Certificates;
    using Google.Apis.Analytics.v3;
    using Google.Apis.Analytics.v3.Data;
    using Google.Apis.Authentication.OAuth2;
    using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
    using Google.Apis.Util;

    class Program
    {
        static void Main(string[] args)
        {
            log4net.Config.XmlConfigurator.Configure();            
            const string ServiceAccountId = "nnnnnnnnnnn.apps.googleusercontent.com";
            const string ServiceAccountUser = "nnnnnnnnnnn@developer.gserviceaccount.com";
            AssertionFlowClient client = new AssertionFlowClient(
                GoogleAuthenticationServer.Description, new X509Certificate2(@"value-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable))
            {
                Scope = AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue(),
                ServiceAccountId = ServiceAccountUser //Bug, why does ServiceAccountUser have to be assigned to ServiceAccountId
                //,ServiceAccountUser = ServiceAccountUser
            };
            OAuth2Authenticator<AssertionFlowClient> authenticator = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);            
            AnalyticsService service = new AnalyticsService(authenticator);            
            string profileId = "ga:64968920";
            string startDate = "2010-10-01";
            string endDate = "2010-10-31";
            string metrics = "ga:visits";
            DataResource.GaResource.GetRequest request = service.Data.Ga.Get(profileId, startDate, endDate, metrics);
            request.Dimensions = "ga:date";
            GaData data = request.Fetch();            
        }

    }
}

#2


4  

I was checking out the analytics API yesterday and noticed how undocumented it is and no samples etc.

我昨天检查了分析API,并注意到它没有文档,没有样本等。

Any ways, I have created a library that you could use to access analytics easily with couple of lines and make direct databinding to DataTables for data returned it's open source on the github so feel free to contribute :)

无论如何,我已经创建了一个库,您可以使用它来轻松访问分析,并使用几行来直接数据绑定到DataTables,并在github上返回它的开源,所以随时可以贡献:)

https://github.com/rmostafa/DotNetAnalyticsAPI

Usage

Analytics.AnalyticsManager manager = new Analytics.AnalyticsManager(Server.MapPath("~/bin/privatekey.p12"), "YOUR_EMAIL");
            manager.LoadAnalyticsProfiles();


List<Analytics.Data.DataItem> metrics = new List<Analytics.Data.DataItem>();
metrics.Add(Analytics.Data.Visitor.Metrics.visitors);
metrics.Add(Analytics.Data.Session.Metrics.visits);
List<Analytics.Data.DataItem> dimensions = new List<Analytics.Data.DataItem>();
dimensions.Add(Analytics.Data.GeoNetwork.Dimensions.country);
dimensions.Add(Analytics.Data.GeoNetwork.Dimensions.city);


System.Data.DataTable table = manager.GetGaDataTable(DateTime.Today.AddDays(-3), DateTime.Today, metrics, dimensions, null, metrics);

There is direct code mapping for All Google API Reporting commands categorized same way like the API so you could it even without reading the API Documentation at all since all features there are documented in the attributes, I have wrote code that parsed the complete api documentation and resourced the Metrics, Dimensions, Calculated Features in an XML that i generated from physical classes that you could use directly like the example above it's fun to play with :) enjoy

所有Google API报告命令的直接代码映射都按照与API类似的方式进行分类,因此即使没有阅读API文档也可以,因为属性中记录了所有功能,我编写的代码解析了完整的api文档和资源中我使用物理类生成的XML中的度量标准,维度,计算特征,您可以直接使用它们,就像上面的示例一样,玩起来很有趣:)享受

https://github.com/rmostafa/DotNetAnalyticsAPI

#3


3  

string scope = Google.Apis.Util.Utilities.GetStringValue(AnalyticsService.Scopes.AnalyticsReadonly);

#4


2  

Here is my working example posted here [1]: Automated use of google-api-dotnet-client with OAuth 2.0 I put a lot the research into finding and piecing the code together hope this saves you some time.

这是我在这里发布的工作示例[1]:使用OAuth 2.0自动使用google-api-dotnet-client我将大量研究工作放在一起寻找并拼凑代码希望这可以节省您一些时间。

#5


1  

Regarding Richard Collette answer, be careful that using his method if you want to use Analytics API in READONLY mode, the correct way to use it is:

关于Richard Collette的回答,如果你想在READONLY模式下使用Analytics API,请注意使用他的方法,使用它的正确方法是:

string Scope = "analytics.readonly"

and not

string Scope = AnalyticsService.Scopes.AnalyticsReadOnly.ToString().ToLower()

as he seems to tell that there is a bug. In fact, the bug is that the .toString() method returns analyticsreadonly and NOT analytics.readonly which is the way that Google likes. That's it.

因为他似乎告诉我有一个错误。事实上,错误是.toString()方法返回analyticsreadonly而不是analytics.readonly这是Google喜欢的方式。而已。