演练:C 中的必应广告 API 桌面应用程序#
此示例 C# 控制台应用程序通过你提供的凭据提示用户同意,然后获取经过身份验证的用户可以访问的帐户。
必须先注册应用程序,并记下) 注册的应用程序 ID (客户端 ID。 有关注册应用程序和授权代码授予流的详细信息,请参阅 使用 OAuth 进行身份验证。
还需要生产 开发人员令牌。 可以按如下所述分步创建示例,也可以从 GitHub 下载更多示例。
代码演练
打开 Visual Studio 开发环境。
通过 File -New ->>Project 创建新项目
在 “新建项目” 窗口中,选择下拉列表中的 “.NET Framework 4.8 ”,然后单击“ 控制台应用” (.NET 框架) 模板。 将项目命名为 BingAdsConsoleApp ,然后单击“ 确定”。
通过 NuGet 为 BingAdsConsoleApp 安装 SDK。 有关依赖项的详细信息,请参阅 安装 SDK。 单击“ 工具” ->“NuGet 包管理器 ”-“>包管理器控制台”。 在提示符下,键入以下命令以一次安装一个包:
Install-Package Microsoft.BingAds.SDK
和Install-Package System.Configuration.ConfigurationManager
。打开 App.config 文件,并将其内容替换为以下代码块。 编辑 BingAdsEnvironment 以从沙盒移动到生产环境。 如果面向生产环境,则必须将 4c0b021c-00c3-4508-838f-d3127e8167ff 替换为注册生产应用程序时预配 的应用程序 ID ,并将 BBD37VB98 替换为生产 开发人员令牌。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="BingAdsConsoleApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" /> </startup> <appSettings> <!-- To use the production environment, set this value to "Production". --> <add key="BingAdsEnvironment" value="Sandbox"/> <add key="ClientSettingsProvider.ServiceUri" value=""/> </appSettings> <userSettings> <BingAdsConsoleApp.Properties.Settings> <setting name="DeveloperToken" serializeAs="String"> <value>BBD37VB98</value> </setting> <setting name="ClientId" serializeAs="String"> <value>4c0b021c-00c3-4508-838f-d3127e8167ff</value> </setting> </BingAdsConsoleApp.Properties.Settings> </userSettings> </configuration>
创建设置文件。 在 BingAdsConsoleApp 的项目视图中,右键单击“ 属性 ”,然后单击“ 打开”。 单击 “设置”,然后单击文本“ 项目不包含默认设置文件”。单击此处创建一个。 将自动添加来自 app.config 的新值。
打开 Program.cs 文件,并将其内容替换为以下代码块。
using System; using System.Linq; using System.Configuration; using System.Net.Http; using System.ServiceModel; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.BingAds; using Microsoft.BingAds.V13.CustomerManagement; using BingAdsConsoleApp.Properties; using System.IO; namespace BingAdsConsoleApp { class Program { private static AuthorizationData _authorizationData; private static ServiceClient<ICustomerManagementService> _customerManagementService; private static string ClientState = "ClientStateGoesHere"; static void Main(string[] args) { try { Authentication authentication = AuthenticateWithOAuth(); // Most Bing Ads API service operations require account and customer ID. // This utiltiy operation sets the global authorization data instance // to the first account that the current authenticated user can access. SetAuthorizationDataAsync(authentication).Wait(); // You can extend the console app with the examples library at: // https://github.com/BingAds/BingAds-dotNet-SDK/tree/main/examples/BingAdsExamples } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("OAuthTokenRequestException Message:\n{0}", ex.Message)); if (ex.Details != null) { OutputStatusMessage(string.Format("OAuthTokenRequestException Details:\nError: {0}\nDescription: {1}", ex.Details.Error, ex.Details.Description)); } } // Catch Customer Management service exceptions catch (FaultException<AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => { if ((error.Code == 105) || (error.Code == 106)) { return "Authorization data is missing or incomplete for the specified environment.\n" + "To run the examples switch users or contact support for help with the following error.\n"; } return string.Format("{0}: {1}", error.Code, error.Message); }))); OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V13.CustomerManagement.ApiFault> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (HttpRequestException ex) { OutputStatusMessage(ex.Message); } } /// <summary> /// Utility method for setting the customer and account identifiers within the global /// <see cref="_authorizationData"/> instance. /// </summary> /// <param name="authentication">The OAuth authentication credentials.</param> /// <returns></returns> private static async Task SetAuthorizationDataAsync(Authentication authentication) { _authorizationData = new AuthorizationData { Authentication = authentication, DeveloperToken = Settings.Default["DeveloperToken"].ToString() }; var apiEnvironment = ConfigurationManager.AppSettings["BingAdsEnvironment"] == ApiEnvironment.Sandbox.ToString() ? ApiEnvironment.Sandbox : ApiEnvironment.Production; _customerManagementService = new ServiceClient<ICustomerManagementService>( _authorizationData, apiEnvironment ); 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; _authorizationData.AccountId = (long)accounts[0].Id; _authorizationData.CustomerId = (int)accounts[0].ParentCustomerId; OutputArrayOfAdvertiserAccount(accounts); return; } /// <summary> /// Authenticates the current user via OAuth. /// </summary> /// <returns>The OAuth authentication instance for a user.</returns> private static Authentication AuthenticateWithOAuth() { var apiEnvironment = ConfigurationManager.AppSettings["BingAdsEnvironment"] == ApiEnvironment.Sandbox.ToString() ? ApiEnvironment.Sandbox : ApiEnvironment.Production; var oAuthDesktopMobileAuthCodeGrant = new OAuthDesktopMobileAuthCodeGrant( Settings.Default["ClientId"].ToString(), apiEnvironment ); // It is recommended that you specify a non guessable 'state' request parameter to help prevent // cross site request forgery (CSRF). oAuthDesktopMobileAuthCodeGrant.State = ClientState; string refreshToken; // If you have previously securely stored a refresh token, try to use it. if (GetRefreshToken(out refreshToken)) { AuthorizeWithRefreshTokenAsync(oAuthDesktopMobileAuthCodeGrant, refreshToken).Wait(); } else { // You must request user consent at least once through a web browser control. Console.WriteLine(string.Format( "Open a new web browser and navigate to {0}\n\n" + "Grant consent in the web browser for the application to access " + "your advertising accounts, and then enter the response URI that includes " + "the authorization 'code' parameter: \n", oAuthDesktopMobileAuthCodeGrant.GetAuthorizationEndpoint()) ); // Request access and refresh tokens using the URI that you provided manually during program execution. var responseUri = new Uri(Console.ReadLine()); if (oAuthDesktopMobileAuthCodeGrant.State != ClientState) throw new HttpRequestException("The OAuth response state does not match the client request state."); oAuthDesktopMobileAuthCodeGrant.RequestAccessAndRefreshTokensAsync(responseUri).Wait(); SaveRefreshToken(oAuthDesktopMobileAuthCodeGrant.OAuthTokens.RefreshToken); } // It is important to save the most recent refresh token whenever new OAuth tokens are received. // You will want to subscribe to the NewOAuthTokensReceived event handler. // When calling Bing Ads API service operations with ServiceClient<TService>, BulkServiceManager, or ReportingServiceManager, // each instance will refresh your access token automatically if they detect the AuthenticationTokenExpired (109) error code. oAuthDesktopMobileAuthCodeGrant.NewOAuthTokensReceived += (sender, tokens) => SaveRefreshToken(tokens.NewRefreshToken); return oAuthDesktopMobileAuthCodeGrant; } /// <summary> /// Requests new access and refresh tokens given an existing refresh token. /// </summary> /// <param name="authentication">The OAuth authentication instance for a user.</param> /// <param name="refreshToken">The previous refresh token.</param> /// <returns></returns> private static Task<OAuthTokens> AuthorizeWithRefreshTokenAsync( OAuthDesktopMobileAuthCodeGrant authentication, string refreshToken) { return authentication.RequestAccessAndRefreshTokensAsync(refreshToken); } /// <summary> /// You should modify the example, and store the refresh token securely. /// </summary> /// <param name="newRefreshtoken">The refresh token to save.</param> private static void SaveRefreshToken(string newRefreshtoken) { if (newRefreshtoken != null) { using (StreamWriter outputFile = new StreamWriter( Environment.CurrentDirectory + @"\refreshtoken.txt", false)) { outputFile.WriteLine(newRefreshtoken); } } } /// <summary> /// Returns the prior refresh token if available. /// </summary> /// <param name="refreshToken"></param> /// <returns>The latest stored refresh token.</returns> private static bool GetRefreshToken(out string refreshToken) { var filePath = Environment.CurrentDirectory + @"\refreshtoken.txt"; if (!File.Exists(filePath)) { refreshToken = null; return false; } String fileContents; using (StreamReader sr = new StreamReader(filePath)) { fileContents = sr.ReadToEnd(); } if (string.IsNullOrEmpty(fileContents)) { refreshToken = null; return false; } try { refreshToken = fileContents; return true; } catch (FormatException) { refreshToken = null; return false; } } #region OutputHelpers /** * You can extend the console app with the example helpers at: * https://github.com/BingAds/BingAds-dotNet-SDK/tree/main/examples/BingAdsExamples **/ 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) { Console.WriteLine(msg); } #endregion OutputHelpers } }
单击“ 生成 ->生成 BingAdsConsoleApp”,然后运行该应用程序。 启动应用程序时,系统会默认提示 Microsoft 帐户凭据在生产环境中进行身份验证。
配置沙盒
若要使用沙盒,请在项目根 App.config文件的 appSettings> 节点内<将 BingAdsEnvironment 密钥设置为沙盒。
<add key="BingAdsEnvironment" value ="Sandbox"/>
还可以为每个 ServiceClient 单独设置环境,如下所示。
_customerManagementService = new ServiceClient<ICustomerManagementService>(
_authorizationData,
ApiEnvironment.Sandbox
);
无论是全局设置还是单独设置 ServiceClient 环境,你还需要将 OAuth 环境设置为沙盒。
var oAuthDesktopMobileAuthCodeGrant = new OAuthDesktopMobileAuthCodeGrant(
ClientId,
ApiEnvironment.Sandbox
);