简体   繁体   中英

Querying eBay API with WCF

I'm trying to work with the eBay API in my ASP.NET Core 2.0 website. eBay's .NET SDK doesn't work with .NET Core, so I have the service added through WCF. I'm brand new to WCF and I can't get this client to work though. No matter what I try I'm getting this error:

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.

I'm specifically attempting to query GetItem . My credentials do work, I was able to confirm that with SoapUI.

This is an example of the code I'm trying to use. What am I missing? How do I make this work?

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

Alright, I managed to figure it out. The way to get it working is a bit of a doozy, there's probably a better way but I couldn't figure one out. Strap in.

WCF Documentation

Before I start, Microsoft has great docs about WCF here . Check them out if you're not familiar with WCF like me. This is using a client , not a server . We're just talking to eBay's server, not running our own.

Step 0: Ensure that solution builds

Before the next step, make sure your solution builds. You cannot add a connected service unless it successfully builds.

Step 1: Add connected service

First step is to add the service reference to the project and generate the model from eBay's server. eBay provides WSDL files for this purpose. We specifically want this one . You can use the Add Connected Service tool in Visual Studio to do this. You must have an ASP.NET project selected, then go to Tools > Add Connected Service . Use the option for Microsoft WCF Web Service Reference Provider . Enter that .wsdl URL in the URI box, click Go , change the namespace to whatever you want, then click Finish . The namespace is purely preference, I made mine EbayService . All other settings can be left as their defaults. A box will pop up with a bunch of logging stuff, you can ignore it and wait until it's finished. There will be a TON of yellow warnings, those can be ignored as well.

(Note: You can also use svcutility to generate the model instead of Visual Studio's GUI, but I won't cover that.)

Step 2: Edit service reference file

The web project should now have a section in Solution Explorer near the top called Connected Services . There should be a folder within that's named whatever namespace you provided previously. Inside will be a file called Reference.cs that contains all of the parsing information for the eBay service. This file is MASSIVE, mine has nearly 122k lines. We need to do a couple of things in here.

We need to do a couple find/replace operations. To do this, we have to find/replace two strings. Turn on regex in the replace window, then replace , Order=\\d{1,3} with nothing. This catches the cases where Order is one of multiple parameters on the attribute. Then do the same thing, but replace Order=\\d{1,3} . Doing this breaks one of the classes that I came across, so find the class ReviseInventoryStatusRequestType . It has a field called any1Field and a property called Any1 . Delete both of those.

Keep in mind you'll need to redo these steps if you ever regenerate the service from the server, as that process overwrites Reference.cs .

(Why do this? The eBay WSDL file specifies the order that elements will be in when they're returned from the API, but they're not even close to correct. Because of this, almost none of the responses will actually parse properly. We need to remove the order specifications and let the program handle the parsing via name.)

Step 3: Write client wrapper

It's possible to work directly with this service, but doing so gets really messy. I therefore wrote a wrapper around the client that simplifies this greatly. Commented code is below, the DI will come in the next step so don't worry about that. You would need to add a function for each API endpoint that you'll use.

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;
    }
}

Step 4: Set up dependency injection

I use the built-in ASP.NET Core DI , so that needs to be set up. The base eBay client class can be a singleton, but your wrapper class should be scoped.

In Startup.cs / ConfigureServices() :

// This is the base client
services.AddSingleton(new eBayAPIInterfaceClient(eBayAPIInterfaceClient.EndpointConfiguration.eBayAPI));

// This is our wrapper
services.AddScoped<EbayClient>();

Step 5: Call the wrapper in your code

Your controller might look like this:

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.
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM