演练:C 中的必应广告 API Web 应用程序#

此示例 C# Web 应用程序通过提供的凭据提示用户同意,然后获取经过身份验证的用户可以访问的帐户。

必须首先注册应用程序,并记下客户端 ID (已注册的应用程序 ID) 、客户端机密 (注册的密码) 和重定向 URI。 有关注册应用程序和授权代码授予流的详细信息,请参阅 使用 OAuth 进行身份验证

还需要生产 开发人员令牌。 可以按如下所述分步创建示例,也可以从 GitHub 下载更多示例。

提示

此示例引用 在 Azure 中创建 ASP.NET Framework Web 应用中的步骤。 有关将 Web 应用部署到 Azure 的更多详细信息,请参阅 Azure 文档。

代码演练

  1. 打开 Visual Studio Community 2017 开发环境。 如果已安装 Visual Studio,请单击“工具>获取工具和功能”,在 Visual Studio 中添加 ASP.NET 和 Web 开发和Azure 开发工作负载。

  2. 通过选择“文件>>新建项目”创建新项目。 在“新建项目”窗口中,选择下拉列表中的“.NET Framework 4.7.1”,然后选择“Visual C# > Web > ASP.NET Web Application (.NET Framework) ”。 将项目命名为 BingAdsWebApp ,然后单击“ 确定”。

  3. 可以将任何类型的 ASP.NET Web 应用部署到 Azure。 对于本快速入门,请选择 MVC 模板,确保身份验证设置为 “无身份验证”,然后单击“ 确定”。

  4. 通过 NuGet 为 BingAdsWebApp 安装 SDK。 有关依赖项的详细信息,请参阅 安装 SDK。 单击“ 工具” ->“NuGet 包管理器 ”-“>包管理器控制台”。 在提示符下,键入以下命令以一次安装一个包: Install-Package Microsoft.BingAds.SDKInstall-Package System.ServiceModel.Primitives -Version 4.4.1Install-Package System.ServiceModel.Http -Version 4.4.1Install-Package System.Configuration.ConfigurationManager -Version 4.4.1

  5. 打开 Web.config 文件,并将其内容替换为以下代码块。 编辑 BingAdsEnvironment 以从沙盒移动到生产环境,并根据需要设置生产 开发人员令牌 。 必须使用注册应用程序时预配的相应应用程序 ID应用程序机密重定向 URL 值编辑 ClientIdClientSecretRedirectUri

注意

如果打算在上线之前在 localhost 上部署,请确保同时注册本地 SSL URL 和端口,例如 https://localhost:44383/. 在这种情况下,还必须在 BingAdsWebApp 项目属性窗口中将 “SSL 启用 ”设置为 True ,然后可以从同一属性窗口复制 localhost 端口。

  <?xml version="1.0" encoding="utf-8"?>
  <!--
      For more information on how to configure your ASP.NET application, please visit
      https://go.microsoft.com/fwlink/?LinkId=301880
      -->
  <configuration>
      <configSections>
      <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <section name="BingAdsWebApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
      </sectionGroup>
      </configSections>
      <appSettings>
      <!-- To use the production environment, set this value to "Production". -->
      <add key="BingAdsEnvironment" value="Sandbox"/>
      <add key="webpages:Version" value="3.0.0.0" />
      <add key="webpages:Enabled" value="false" />
      <add key="ClientValidationEnabled" value="true" />
      <add key="UnobtrusiveJavaScriptEnabled" value="true" />
      </appSettings>
      <system.web>
      <compilation debug="true" targetFramework="4.7.1" />
      <httpRuntime targetFramework="4.7.1" />
      <httpModules>
          <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
      </httpModules>
      </system.web>
      <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
          <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
          <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
          </dependentAssembly>
          <dependentAssembly>
          <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
          <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
          </dependentAssembly>
          <dependentAssembly>
          <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
          <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
          </dependentAssembly>
          <dependentAssembly>
          <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
          <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
          </dependentAssembly>
          <dependentAssembly>
          <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
          <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
          </dependentAssembly>
          <dependentAssembly>
          <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
          <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
          </dependentAssembly>
      </assemblyBinding>
      </runtime>
      <system.webServer>
      <validation validateIntegratedModeConfiguration="false" />
      <modules>
          <remove name="ApplicationInsightsWebTracking" />
          <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
      </modules>
      </system.webServer>
      <system.codedom>
      <compilers>
          <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
          <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
      </compilers>
      </system.codedom>
      <applicationSettings>
      <BingAdsWebApp.Properties.Settings>
          <setting name="RefreshToken" serializeAs="String">
          <value />
          </setting>
          <setting name="ClientId" serializeAs="String">
          <value>ClientIdGoesHere</value>
          </setting>
          <setting name="ClientSecret" serializeAs="String">
          <value>ClientSecretGoesHere</value>
          </setting>
          <setting name="RedirectionUri" serializeAs="String">
          <value>RedirectionUriGoesHere</value>
          </setting>
          <setting name="DeveloperToken" serializeAs="String">
          <value>DeveloperTokenGoesHere</value>
          </setting>
      </BingAdsWebApp.Properties.Settings>
      </applicationSettings>
  </configuration>
  1. 创建设置文件。 在 BingAdsWebApp 的项目视图中,右键单击“ 属性 ”,然后单击“ 打开”。 单击 “设置”,然后单击文本“ 项目不包含默认设置文件”。单击此处创建一个。 将自动添加来自 Web.config 的新值。

  2. 在 BingAdsWebApp 项目的 Views ->Home 文件夹中,打开 Index.cshtml 文件,并将其内容替换为以下代码块。 这定义了一个网页视图,该视图显示将在下面进一步写入的服务调用的结果。

    @{
        ViewBag.Title = "Index";
    }
    
    <h2>Index</h2>
    
    <h4>Accounts</h4>
    <p>@Html.Raw(@ViewBag.Accounts)</p>
    
    <p><b style="color: red">@ViewBag.Errors</b></p>
    
  3. 在 BingAdsWebApp 项目的 Controllers 文件夹中,打开 HomeController.cs 文件,并将其内容替换为以下代码块。 这将定义服务调用,该调用将确定哪些结果显示在上面定义的视图中。

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Net.Http;
    using System.ServiceModel;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    using BingAdsWebApp.Properties;
    using Microsoft.BingAds;
    using Microsoft.BingAds.V13.CustomerManagement;
    
    namespace BingAdsWebApp.Controllers
    {
        public class HomeController : Controller
        {
            private static AuthorizationData _authorizationData;
            private static ServiceClient<ICustomerManagementService> _customerManagementService;
            private static string ClientState = "ClientStateGoesHere";
            private static string _output = "";
    
            /// <summary>
            /// Controls the contents displayed at Index.cshtml.
            /// </summary>
            public async Task<ActionResult> Index()
            {
                try
                {
                    // If there is already an authenticated Microsoft account during this HTTP session, 
                    // go ahead and call Bing Ads API service operations.
    
                    if (Session["auth"] != null)
                    {
                        return await SetAuthorizationDataAsync((OAuthWebAuthCodeGrant)Session["auth"]);
                    }
    
                    // Prepare the OAuth object for use with the authorization code grant flow. 
    
                    var apiEnvironment =
                        ConfigurationManager.AppSettings["BingAdsEnvironment"] == ApiEnvironment.Sandbox.ToString() ?
                        ApiEnvironment.Sandbox : ApiEnvironment.Production;
                    var oAuthWebAuthCodeGrant = new OAuthWebAuthCodeGrant(
                        Settings.Default["ClientId"].ToString(),
                        Settings.Default["ClientSecret"].ToString(), 
                        new Uri(Settings.Default["RedirectionUri"].ToString()),
                        apiEnvironment);
    
                    // It is recommended that you specify a non guessable 'state' request parameter to help prevent
                    // cross site request forgery (CSRF). 
                    oAuthWebAuthCodeGrant.State = ClientState;
    
                    // When calling Bing Ads API service operations with ServiceClient or BulkServiceManager, each will refresh your access token 
                    // automatically if they detect the AuthenticationTokenExpired (109) error code. 
                    // As a best practice you should always use the most recent provided refresh token.
                    // Save the refresh token whenever new OAuth tokens are received by subscribing to the NewOAuthTokensReceived event handler. 
    
                    oAuthWebAuthCodeGrant.NewOAuthTokensReceived +=
                        (sender, args) => SaveRefreshToken(args.NewRefreshToken);
    
                    // If a refresh token is already present, use it to request new access and refresh tokens.
    
                    if (RefreshTokenExists())
                    {
                        await oAuthWebAuthCodeGrant.RequestAccessAndRefreshTokensAsync(GetRefreshToken());
    
                        // Save the authentication object in a session for future requests.
                        Session["auth"] = oAuthWebAuthCodeGrant;
    
                        return await SetAuthorizationDataAsync((OAuthWebAuthCodeGrant)Session["auth"]);
                    }
    
                    // If the current HTTP request is a callback from the Microsoft Account authorization server,
                    // use the current request url containing authorization code to request new access and refresh tokens
                    if (Request["code"] != null)
                    {
                        if (oAuthWebAuthCodeGrant.State != ClientState)
                            throw new HttpRequestException("The OAuth response state does not match the client request state.");
    
                        await oAuthWebAuthCodeGrant.RequestAccessAndRefreshTokensAsync(Request.Url);
    
                        // Save the authentication object in a session for future requests. 
                        Session["auth"] = oAuthWebAuthCodeGrant;
    
                        return await SetAuthorizationDataAsync((OAuthWebAuthCodeGrant)Session["auth"]);
                    }
    
                    SaveRefreshToken(oAuthWebAuthCodeGrant.OAuthTokens?.RefreshToken);
    
                    // If there is no refresh token saved and no callback from the authorization server, 
                    // then connect to the authorization server and request user consent. 
                    return Redirect(oAuthWebAuthCodeGrant.GetAuthorizationEndpoint().ToString());
                }
                // Catch authentication exceptions
                catch (OAuthTokenRequestException ex)
                {
                    ViewBag.Errors = (string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", 
                        ex.Details.Error, ex.Details.Description));
                    return View();
                }
                // Catch Customer Management service exceptions
                catch (FaultException<Microsoft.BingAds.V13.CustomerManagement.AdApiFaultDetail> ex)
                {
                    ViewBag.Errors = (string.Join("; ", ex.Detail.Errors.Select(
                        error => string.Format("{0}: {1}", error.Code, error.Message))));
                    return View();
                }
                catch (FaultException<Microsoft.BingAds.V13.CustomerManagement.ApiFault> ex)
                {
                    ViewBag.Errors = (string.Join("; ", ex.Detail.OperationErrors.Select(
                        error => string.Format("{0}: {1}", error.Code, error.Message))));
                    return View();
                }            
                catch (Exception ex)
                {
                    ViewBag.Errors = ex.Message;
                    return View();
                }
            }
    
            /// <summary>
            /// Adds a campaign to an account of the current authenticated user. 
            /// </summary>
            private async Task<ActionResult> SetAuthorizationDataAsync(Authentication authentication)
            {
                _authorizationData = new AuthorizationData
                {
                    Authentication = authentication,
                    DeveloperToken = Settings.Default["DeveloperToken"].ToString()
                };
    
                _customerManagementService = new ServiceClient<ICustomerManagementService>(_authorizationData);
    
                var getUserRequest = new GetUserRequest
                {
                    UserId = null
                };
    
                var getUserResponse = 
                    (await _customerManagementService.CallAsync((s, r) => s.GetUserAsync(r), getUserRequest));
                var user = getUserResponse.User;
    
                var predicate = new Predicate
                {
                    Field = "UserId",
                    Operator = PredicateOperator.Equals,
                    Value = user.Id.ToString()
                };
    
                var paging = new Paging
                {
                    Index = 0,
                    Size = 10
                };
    
                var searchAccountsRequest = new SearchAccountsRequest
                {
                    Ordering = null,
                    PageInfo = paging,
                    Predicates = new[] { predicate }
                };
    
                var searchAccountsResponse =
                    (await _customerManagementService.CallAsync((s, r) => s.SearchAccountsAsync(r), searchAccountsRequest));
    
                var accounts = searchAccountsResponse.Accounts.ToArray();
                if (accounts.Length <= 0) return View();
    
                _authorizationData.AccountId = (long)accounts[0].Id;
                _authorizationData.CustomerId = (int)accounts[0].ParentCustomerId;
    
                OutputArrayOfAdvertiserAccount(accounts);
    
                ViewBag.Accounts = _output;
                _output = null;
    
                return View();
            }
    
            /// <summary>
            /// Saves the refresh token
            /// </summary>
            /// <param name="refreshToken">The refresh token to save</param>
            private static void SaveRefreshToken(string refreshToken)
            {
                Settings.Default["RefreshToken"] = refreshToken;
                Settings.Default.Save();
            }
    
            /// <summary>
            /// Deletes the contents in the refresh token file
            /// </summary>
            private static void DeleteRefreshToken()
            {
                Settings.Default["RefreshToken"] = "";
                Settings.Default.Save();
            }
    
            /// <summary>
            /// Determines whether the global refresh token exists.
            /// </summary>
            /// <returns>Returns true if the global refresh token exists.</returns>
            private bool RefreshTokenExists()
            {
                return Settings.Default["RefreshToken"] != null 
                    && Settings.Default["RefreshToken"].ToString().Length > 0;
            }
    
            /// <summary>
            /// Gets the global refresh token.
            /// </summary>
            /// <returns>The global refresh token.</returns>
            private string GetRefreshToken()
            {
                return Settings.Default["RefreshToken"].ToString();
            }
    
            #region OutputHelpers
    
            /**
             * You can extend the app with example output helpers at:
             * https://github.com/BingAds/BingAds-dotNet-SDK/tree/main/examples/BingAdsExamples/BingAdsExamplesLibrary/v13
             * 
             * AdInsightExampleHelper.cs
             * BulkExampleHelper.cs
             * CampaignManagementExampleHelper.cs
             * CustomerBillingExampleHelper.cs
             * CustomerManagementExampleHelper.cs
             * ReportingExampleHelper.cs
             **/
    
            private static void OutputArrayOfAdvertiserAccount(IList<AdvertiserAccount> dataObjects)
            {
                if (null != dataObjects)
                {
                    foreach (var dataObject in dataObjects)
                    {
                        OutputAdvertiserAccount(dataObject);
                        OutputStatusMessage("\n");
                    }
                }
            }
    
            private static void OutputAdvertiserAccount(AdvertiserAccount dataObject)
            {
                if (null != dataObject)
                {
                    OutputStatusMessage(string.Format("BillToCustomerId: {0}", dataObject.BillToCustomerId));
                    OutputStatusMessage(string.Format("CurrencyCode: {0}", dataObject.CurrencyCode));
                    OutputStatusMessage(string.Format("AccountFinancialStatus: {0}", dataObject.AccountFinancialStatus));
                    OutputStatusMessage(string.Format("Id: {0}", dataObject.Id));
                    OutputStatusMessage(string.Format("Language: {0}", dataObject.Language));
                    OutputStatusMessage(string.Format("LastModifiedByUserId: {0}", dataObject.LastModifiedByUserId));
                    OutputStatusMessage(string.Format("LastModifiedTime: {0}", dataObject.LastModifiedTime));
                    OutputStatusMessage(string.Format("Name: {0}", dataObject.Name));
                    OutputStatusMessage(string.Format("Number: {0}", dataObject.Number));
                    OutputStatusMessage(string.Format("ParentCustomerId: {0}", dataObject.ParentCustomerId));
                    OutputStatusMessage(string.Format("PaymentMethodId: {0}", dataObject.PaymentMethodId));
                    OutputStatusMessage(string.Format("PaymentMethodType: {0}", dataObject.PaymentMethodType));
                    OutputStatusMessage(string.Format("PrimaryUserId: {0}", dataObject.PrimaryUserId));
                    OutputStatusMessage(string.Format("AccountLifeCycleStatus: {0}", dataObject.AccountLifeCycleStatus));
                    OutputStatusMessage(string.Format("TimeStamp: {0}", dataObject.TimeStamp));
                    OutputStatusMessage(string.Format("TimeZone: {0}", dataObject.TimeZone));
                    OutputStatusMessage(string.Format("PauseReason: {0}", dataObject.PauseReason));
                    OutputArrayOfKeyValuePairOfstringstring(dataObject.ForwardCompatibilityMap);
                    OutputArrayOfCustomerInfo(dataObject.LinkedAgencies);
                    OutputStatusMessage(string.Format("SalesHouseCustomerId: {0}", dataObject.SalesHouseCustomerId));
                    OutputArrayOfKeyValuePairOfstringstring(dataObject.TaxInformation);
                    OutputStatusMessage(string.Format("BackUpPaymentInstrumentId: {0}", dataObject.BackUpPaymentInstrumentId));
                    OutputStatusMessage(string.Format("BillingThresholdAmount: {0}", dataObject.BillingThresholdAmount));
                    OutputAddress(dataObject.BusinessAddress);
                    OutputStatusMessage(string.Format("AutoTagType: {0}", dataObject.AutoTagType));
                    OutputStatusMessage(string.Format("SoldToPaymentInstrumentId: {0}", dataObject.SoldToPaymentInstrumentId));
                }
            }
    
            private static void OutputAddress(Address dataObject)
            {
                if (null != dataObject)
                {
                    OutputStatusMessage(string.Format("City: {0}", dataObject.City));
                    OutputStatusMessage(string.Format("CountryCode: {0}", dataObject.CountryCode));
                    OutputStatusMessage(string.Format("Id: {0}", dataObject.Id));
                    OutputStatusMessage(string.Format("Line1: {0}", dataObject.Line1));
                    OutputStatusMessage(string.Format("Line2: {0}", dataObject.Line2));
                    OutputStatusMessage(string.Format("Line3: {0}", dataObject.Line3));
                    OutputStatusMessage(string.Format("Line4: {0}", dataObject.Line4));
                    OutputStatusMessage(string.Format("PostalCode: {0}", dataObject.PostalCode));
                    OutputStatusMessage(string.Format("StateOrProvince: {0}", dataObject.StateOrProvince));
                    OutputStatusMessage(string.Format("TimeStamp: {0}", dataObject.TimeStamp));
                    OutputStatusMessage(string.Format("BusinessName: {0}", dataObject.BusinessName));
                }
            }
    
            private static void OutputArrayOfKeyValuePairOfstringstring(IList<KeyValuePair<string, string>> dataObjects)
            {
                if (null != dataObjects)
                {
                    foreach (var dataObject in dataObjects)
                    {
                        OutputKeyValuePairOfstringstring(dataObject);
                    }
                }
            }
    
            private static void OutputKeyValuePairOfstringstring(KeyValuePair<string, string> dataObject)
            {
                if (null != dataObject.Key)
                {
                    OutputStatusMessage(string.Format("key: {0}", dataObject.Key));
                    OutputStatusMessage(string.Format("value: {0}", dataObject.Value));
                }
            }
    
            private static void OutputCustomerInfo(CustomerInfo dataObject)
            {
                if (null != dataObject)
                {
                    OutputStatusMessage(string.Format("Id: {0}", dataObject.Id));
                    OutputStatusMessage(string.Format("Name: {0}", dataObject.Name));
                }
            }
    
            private static void OutputArrayOfCustomerInfo(IList<CustomerInfo> dataObjects)
            {
                if (null != dataObjects)
                {
                    foreach (var dataObject in dataObjects)
                    {
                        OutputCustomerInfo(dataObject);
                        OutputStatusMessage("\n");
                    }
                }
            }
    
            private static void OutputStatusMessage(String msg)
            {
                _output += (msg + "<br/>");
            }
    
            #endregion OutputHelpers
        }
    }
    
  4. 若要首先在本地计算机上部署 Web 应用,必须在 BingAdsWebApp 项目属性窗口中将 “SSL 已启用” 设置为 True 。 如果尚未这样做,则已注册的应用程序重定向 URL 必须包括 SSL URL 和端口,例如 https://localhost:44383/. 从菜单中,选择“ 调试”“开始” > (不调试) 以在本地运行 Web 应用。

  5. 若要使用 Azure 应用服务实时部署 Web 应用请参阅 Azure 文档中的订阅和部署说明,例如,在 Azure 中创建 ASP.NET Framework Web 应用

配置沙盒

若要使用沙盒,请在项目根 Web.config文件的 appSettings> 节点内<BingAdsEnvironment 密钥设置为沙盒。

<add key="BingAdsEnvironment" value ="Sandbox"/>

还可以为每个 ServiceClient 单独设置环境,如下所示。

_customerService = new ServiceClient<ICustomerManagementService>(_authorizationData, ApiEnvironment.Sandbox);

无论是全局设置还是单独设置 ServiceClient 环境,你还需要将 OAuth 环境设置为沙盒。

var oAuthWebAuthCodeGrant = new OAuthWebAuthCodeGrant(ClientId, ClientSecret, new Uri(RedirectionUri), ApiEnvironment.Sandbox);

另请参阅

沙盒
必应广告 API 代码示例
必应广告 API Web 服务地址