使用 WCF 查询 eBay API
Querying eBay API with WCF
我正尝试在我的 ASP.NET Core 2.0 网站中使用 eBay API。 eBay 的 .NET SDK 不适用于 .NET Core,因此我通过 WCF 添加了该服务。我是 WCF 的新手,但我无法让这个客户工作。无论我尝试什么,我都会收到此错误:
Exception thrown: 'System.ServiceModel.FaultException' in System.Private.CoreLib.dll
com.ebay.app.pres.service.hosting.WebServiceDisabledException: The web service eBayAPI is not properly configured or not found and is disabled.
我专门想查询 GetItem
。我的凭据确实有效,我能够使用 SoapUI 进行确认。
这是我尝试使用的代码示例。我错过了什么?我该怎么做?
var rawEbayConfig = Configuration.GetSection("Ebay");
var ebayConfig = rawEbayConfig.Get<EbayConfig>();
// https://api.sandbox.ebay.com/wsapi
var ebayEndpoint = new EndpointAddress(ebayConfig.ApiBaseUrl);
var ebayCreds = new CustomSecurityHeaderType
{
Credentials = new UserIdPasswordType
{
AppId = ebayConfig.AppId,
DevId = ebayConfig.DevId,
AuthCert = ebayConfig.CertId
},
eBayAuthToken = ebayConfig.Token
};
var ebayClient = new eBayAPIInterfaceClient(
eBayAPIInterfaceClient.EndpointConfiguration.eBayAPI, ebayEndpoint);
var reqType = new GetItemRequestType
{
ItemID = "validItemId",
Version = "809"
};
var itemResp = await ebayClient.GetItemAsync(_header, reqType); // error thrown here
// do more stuff
好吧,我想通了。让它工作的方法有点笨拙,可能有更好的方法,但我想不出一个。系好安全带。
WCF 文档
在我开始之前,Microsoft 有关于 WCF 的很棒的文档 here。如果您像我一样不熟悉 WCF,请查看它们。这是使用 client,而不是 server。我们只是在与 eBay 的服务器对话,而不是 运行 我们自己的服务器。
第 0 步:确保构建解决方案
在下一步之前,请确保您的解决方案已生成。除非成功构建,否则您无法添加连接的服务。
第 1 步:添加连接的服务
第一步是将服务引用添加到项目并从 eBay 的服务器生成模型。 eBay 为此提供了 WSDL 文件。我们特别想要 this one。您可以使用 Visual Studio 中的 Add Connected Service
工具来执行此操作。您必须选择一个 ASP.NET 项目,然后转到 Tools > Add Connected Service
。使用 Microsoft WCF Web Service Reference Provider
的选项。在 URI
框中输入 .wsdl URL,单击 Go
,将命名空间更改为您想要的任何名称,然后单击 Finish
。命名空间纯粹是偏好,我自己做了 EbayService
。所有其他设置都可以保留为默认值。将弹出一个框,其中包含一堆日志记录内容,您可以忽略它并等待它完成。会有一大堆黄色警告,这些也可以忽略。
(注意:您也可以使用 svcutility
生成模型而不是 Visual Studio 的 GUI,但我不会介绍它。)
第 2 步:编辑服务参考文件
Web 项目现在应该在靠近顶部的解决方案资源管理器中有一个名为 Connected Services
的部分。里面应该有一个名为您之前提供的命名空间的文件夹。里面将是一个名为 Reference.cs
的文件,其中包含 eBay 服务的所有解析信息。这个文件很大,我的有将近 122k 行。我们需要在这里做几件事。
我们需要进行一些 find/replace 操作。为此,我们必须 find/replace 两个字符串。 在替换 window、 中启用正则表达式,然后将 , Order=\d{1,3}
替换为空。这捕获了 Order
是属性的多个参数之一的情况。然后做同样的事情,但替换 Order=\d{1,3}
。这样做会破坏我遇到的 classes 之一,所以找到 class ReviseInventoryStatusRequestType
。它有一个名为 any1Field
的字段和一个名为 Any1
的 属性。删除这两个。
请记住,如果您从服务器重新生成服务,则需要重做这些步骤,因为该过程会覆盖 Reference.cs
.
(为什么这样做?eBay WSDL 文件指定了元素从 API 返回时的顺序,但它们甚至不接近正确。因此,几乎 none 的响应实际上会正确解析。我们需要删除顺序规范,让程序通过名称处理解析。)
第 3 步:编写客户端包装器
可以直接使用此服务,但这样做会真的变得混乱。因此,我围绕客户端编写了一个包装器,大大简化了这一点。注释代码在下面,DI 将在下一步中出现,所以不用担心。您需要为每个要使用的 API 端点添加一个函数。
public class EbayClient
{
// Don't do this, use a proper method of storing app secrets.
// I have it this way for simplicity in this example.
const string AppId = "MyAppId";
const string DevId = "MyDevId";
const string CertId = "MyCertId";
const string AuthToken = "MyAuthToken";
// This is the version of the API that your WSDL file is from. As of this answer
// the latest version is 1039. All calls need this passed as a parameter.
const string Version = "1039";
// This is the base URL for the API.
// Sandbox: https://api.sandbox.ebay.com/wsapi
// Production: https://api.ebay.com/wsapi
const string BaseApiUrl = "https://api.sandbox.ebay.com/wsapi";
// This is the actual client from the service we just imported. It's being injected
// here via the built-in DI in ASP.NET Core.
readonly eBayAPIInterfaceClient _ebay;
// All of the functions in this class need these credentials passed, so declare it in
// the constructor to make things easier.
readonly CustomSecurityHeaderType _creds;
public EbayClient(eBayAPIInterfaceClient ebay)
{
_ebay = ebay;
_creds = new CustomSecurityHeaderType
{
Credentials = new UserIdPasswordType
{
AppId = AppId,
DevId = DevId,
AuthCert = CertId
},
eBayAuthToken = AuthToken
};
}
// This is a wrapper around the API GetItem call.
public async Task<GetItemResponse> GetItemAsync(string itemId)
{
// All of the API requests and responses use their own type of object.
// This one, naturally, uses GetItemRequest and GetItemResponse.
var reqType = new GetItemRequest
{
GetItemRequest1 = new GetItemRequestType
{
ItemID = itemId,
Version = Version
},
RequesterCredentials = _creds
};
// The service isn't smart enough to know the endpoint URLs itself, so
// we have to set it explicitly.
var addr = new EndpointAddress($"{ApiBaseUrl}?callname=GetItem");
// This creates a channel from the built-in client's ChannelFactory.
// See the WCF docs for explanation of this step.
var ch = _ebay.ChannelFactory.CreateChannel(addr);
// Actually call the API now
var itemResp = await ch.GetItemAsync(reqType);
// Check that the call was a success
if (itemResp.GetItemResponse1.Ack == AckCodeType.Success)
{
// The call succeeded, so handle the data however you want. I created
// a class to simplify the API class because the API class is massive.
return new EbayItem
{
ItemId = itemResp.GetItemResponse1.Item.ItemID,
Price = Convert.ToDecimal(itemResp.GetItemResponse1.Item.BuyItNowPrice.Value),
QuantityAvailable = itemResp.GetItemResponse1.Item.QuantityAvailable
};
}
// Handle an API error however you want. Throw an
// exception or return null, whatever works for you.
return null;
}
}
第 4 步:设置依赖注入
我用的是built-in ASP.NET Core DI,所以需要设置一下。基础 eBay 客户端 class 可以是单例,但您的包装器 class 应该是有作用域的。
在Startup.cs
/ ConfigureServices()
:
// This is the base client
services.AddSingleton(new eBayAPIInterfaceClient(eBayAPIInterfaceClient.EndpointConfiguration.eBayAPI));
// This is our wrapper
services.AddScoped<EbayClient>();
第 5 步:在您的代码中调用包装器
您的控制器可能如下所示:
public class ProductsIdExternalController : Controller
{
// This is the wrapper class
readonly EbayClient _ebay;
public ProductsIdExternalController(EbayClient ebay)
{
_ebay = ebay;
}
[HttpGet]
public async Task<IActionResult> Index(string itemId)
{
var item = await _ebay.GetItemAsync(itemId);
// Use the item however you want.
// Make sure to handle errors in case the item ID doesn't exist.
}
}
我正尝试在我的 ASP.NET Core 2.0 网站中使用 eBay API。 eBay 的 .NET SDK 不适用于 .NET Core,因此我通过 WCF 添加了该服务。我是 WCF 的新手,但我无法让这个客户工作。无论我尝试什么,我都会收到此错误:
Exception thrown: 'System.ServiceModel.FaultException' in System.Private.CoreLib.dll
com.ebay.app.pres.service.hosting.WebServiceDisabledException: The web service eBayAPI is not properly configured or not found and is disabled.
我专门想查询 GetItem
。我的凭据确实有效,我能够使用 SoapUI 进行确认。
这是我尝试使用的代码示例。我错过了什么?我该怎么做?
var rawEbayConfig = Configuration.GetSection("Ebay");
var ebayConfig = rawEbayConfig.Get<EbayConfig>();
// https://api.sandbox.ebay.com/wsapi
var ebayEndpoint = new EndpointAddress(ebayConfig.ApiBaseUrl);
var ebayCreds = new CustomSecurityHeaderType
{
Credentials = new UserIdPasswordType
{
AppId = ebayConfig.AppId,
DevId = ebayConfig.DevId,
AuthCert = ebayConfig.CertId
},
eBayAuthToken = ebayConfig.Token
};
var ebayClient = new eBayAPIInterfaceClient(
eBayAPIInterfaceClient.EndpointConfiguration.eBayAPI, ebayEndpoint);
var reqType = new GetItemRequestType
{
ItemID = "validItemId",
Version = "809"
};
var itemResp = await ebayClient.GetItemAsync(_header, reqType); // error thrown here
// do more stuff
好吧,我想通了。让它工作的方法有点笨拙,可能有更好的方法,但我想不出一个。系好安全带。
WCF 文档
在我开始之前,Microsoft 有关于 WCF 的很棒的文档 here。如果您像我一样不熟悉 WCF,请查看它们。这是使用 client,而不是 server。我们只是在与 eBay 的服务器对话,而不是 运行 我们自己的服务器。
第 0 步:确保构建解决方案
在下一步之前,请确保您的解决方案已生成。除非成功构建,否则您无法添加连接的服务。
第 1 步:添加连接的服务
第一步是将服务引用添加到项目并从 eBay 的服务器生成模型。 eBay 为此提供了 WSDL 文件。我们特别想要 this one。您可以使用 Visual Studio 中的 Add Connected Service
工具来执行此操作。您必须选择一个 ASP.NET 项目,然后转到 Tools > Add Connected Service
。使用 Microsoft WCF Web Service Reference Provider
的选项。在 URI
框中输入 .wsdl URL,单击 Go
,将命名空间更改为您想要的任何名称,然后单击 Finish
。命名空间纯粹是偏好,我自己做了 EbayService
。所有其他设置都可以保留为默认值。将弹出一个框,其中包含一堆日志记录内容,您可以忽略它并等待它完成。会有一大堆黄色警告,这些也可以忽略。
(注意:您也可以使用 svcutility
生成模型而不是 Visual Studio 的 GUI,但我不会介绍它。)
第 2 步:编辑服务参考文件
Web 项目现在应该在靠近顶部的解决方案资源管理器中有一个名为 Connected Services
的部分。里面应该有一个名为您之前提供的命名空间的文件夹。里面将是一个名为 Reference.cs
的文件,其中包含 eBay 服务的所有解析信息。这个文件很大,我的有将近 122k 行。我们需要在这里做几件事。
我们需要进行一些 find/replace 操作。为此,我们必须 find/replace 两个字符串。 在替换 window、 中启用正则表达式,然后将 , Order=\d{1,3}
替换为空。这捕获了 Order
是属性的多个参数之一的情况。然后做同样的事情,但替换 Order=\d{1,3}
。这样做会破坏我遇到的 classes 之一,所以找到 class ReviseInventoryStatusRequestType
。它有一个名为 any1Field
的字段和一个名为 Any1
的 属性。删除这两个。
请记住,如果您从服务器重新生成服务,则需要重做这些步骤,因为该过程会覆盖 Reference.cs
.
(为什么这样做?eBay WSDL 文件指定了元素从 API 返回时的顺序,但它们甚至不接近正确。因此,几乎 none 的响应实际上会正确解析。我们需要删除顺序规范,让程序通过名称处理解析。)
第 3 步:编写客户端包装器
可以直接使用此服务,但这样做会真的变得混乱。因此,我围绕客户端编写了一个包装器,大大简化了这一点。注释代码在下面,DI 将在下一步中出现,所以不用担心。您需要为每个要使用的 API 端点添加一个函数。
public class EbayClient
{
// Don't do this, use a proper method of storing app secrets.
// I have it this way for simplicity in this example.
const string AppId = "MyAppId";
const string DevId = "MyDevId";
const string CertId = "MyCertId";
const string AuthToken = "MyAuthToken";
// This is the version of the API that your WSDL file is from. As of this answer
// the latest version is 1039. All calls need this passed as a parameter.
const string Version = "1039";
// This is the base URL for the API.
// Sandbox: https://api.sandbox.ebay.com/wsapi
// Production: https://api.ebay.com/wsapi
const string BaseApiUrl = "https://api.sandbox.ebay.com/wsapi";
// This is the actual client from the service we just imported. It's being injected
// here via the built-in DI in ASP.NET Core.
readonly eBayAPIInterfaceClient _ebay;
// All of the functions in this class need these credentials passed, so declare it in
// the constructor to make things easier.
readonly CustomSecurityHeaderType _creds;
public EbayClient(eBayAPIInterfaceClient ebay)
{
_ebay = ebay;
_creds = new CustomSecurityHeaderType
{
Credentials = new UserIdPasswordType
{
AppId = AppId,
DevId = DevId,
AuthCert = CertId
},
eBayAuthToken = AuthToken
};
}
// This is a wrapper around the API GetItem call.
public async Task<GetItemResponse> GetItemAsync(string itemId)
{
// All of the API requests and responses use their own type of object.
// This one, naturally, uses GetItemRequest and GetItemResponse.
var reqType = new GetItemRequest
{
GetItemRequest1 = new GetItemRequestType
{
ItemID = itemId,
Version = Version
},
RequesterCredentials = _creds
};
// The service isn't smart enough to know the endpoint URLs itself, so
// we have to set it explicitly.
var addr = new EndpointAddress($"{ApiBaseUrl}?callname=GetItem");
// This creates a channel from the built-in client's ChannelFactory.
// See the WCF docs for explanation of this step.
var ch = _ebay.ChannelFactory.CreateChannel(addr);
// Actually call the API now
var itemResp = await ch.GetItemAsync(reqType);
// Check that the call was a success
if (itemResp.GetItemResponse1.Ack == AckCodeType.Success)
{
// The call succeeded, so handle the data however you want. I created
// a class to simplify the API class because the API class is massive.
return new EbayItem
{
ItemId = itemResp.GetItemResponse1.Item.ItemID,
Price = Convert.ToDecimal(itemResp.GetItemResponse1.Item.BuyItNowPrice.Value),
QuantityAvailable = itemResp.GetItemResponse1.Item.QuantityAvailable
};
}
// Handle an API error however you want. Throw an
// exception or return null, whatever works for you.
return null;
}
}
第 4 步:设置依赖注入
我用的是built-in ASP.NET Core DI,所以需要设置一下。基础 eBay 客户端 class 可以是单例,但您的包装器 class 应该是有作用域的。
在Startup.cs
/ ConfigureServices()
:
// This is the base client
services.AddSingleton(new eBayAPIInterfaceClient(eBayAPIInterfaceClient.EndpointConfiguration.eBayAPI));
// This is our wrapper
services.AddScoped<EbayClient>();
第 5 步:在您的代码中调用包装器
您的控制器可能如下所示:
public class ProductsIdExternalController : Controller
{
// This is the wrapper class
readonly EbayClient _ebay;
public ProductsIdExternalController(EbayClient ebay)
{
_ebay = ebay;
}
[HttpGet]
public async Task<IActionResult> Index(string itemId)
{
var item = await _ebay.GetItemAsync(itemId);
// Use the item however you want.
// Make sure to handle errors in case the item ID doesn't exist.
}
}