question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Better support for POST and PUT requests.

See original GitHub issue

ShopifySharp runs into an issue that many C# libs find themselves facing: non-nullable types and POST/PUT requests don’t mesh that well. For example, take the following class:

public class MyObject
{
  public int Id { get; set; }
  public DateTime Created { get; set; }
  public double Price { get; set; }
  public bool Active { get; set; }
}

Suppose that we need to create a “MyObject” through the Shopify API, but the POST option expects just the Price property. We create a new “MyObject” like so:

var myObj = new MyObject() { Price = 123.00 };

And we send that object to our pretend API. However, because of the way C# works with non-nullable types, C# will send the following:

{ "id": 0, "created": "0001-01-01T00:00:00", "price": 123.00, "active": false }

That’s not good. The API only wanted the price, but C# sent an empty Id and Created date, and a false Active flag too. This isn’t a huge problem when using POST endpoints because Shopify will typically just ignore the extra stuff it isn’t looking for.

The big problem is working with updates to objects through PUT requests. Suppose you now want to update just the Active property for an object that was already created. You create your object like so:

var myUpdatedObj = new MyObject() { Active = true };

But when you send it, C# serializes it into this:

{ "id": 0, "created": "0001-01-01T00:00:00", "price": 0, "active": true }

Now that’s a big problem. Active got set to true, but the object’s price was just set to 0 too. You probably didn’t want to do that, but thanks to C#'s non-nullable types it happened anyway without any input from you. The only way to avoid doing this is to pull the object from the API first, then make changes to that object and send the whole object through the update.

That’s far from ideal; now updating an object goes from one request to two, and wastes bandwidth too. For ShopifySharp 3.0, I’m looking into ways to prevent sending unnecessary information through POST and PUT requests. I have two solutions right now, but welcome any suggestions:

  1. Make all non-string properties nullable. Drawback is that you now have to check if a prop has a value when operating on an object (e.g. Created.Value.ToShortDateString() instead of Created.ToShortDateString()).
  2. Create separate classes for creating and updating an object, akin to the way Stripe.net handles create/update requests. Drawback is a lot of duplicated code, and making changes to one object will need to be reflected to its create/update objects too.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
nozzlegearcommented, Sep 13, 2016

Thanks for the suggestion @vinhch. Right now we’re considering doing exactly what you’re suggesting as one of the solutions to this problem.

Regarding the rate limit, I don’t have any plans to add auto-retry to the lib as I believe that’s better handled by the developer for their specific use-case. You should be able to quickly implement an auto-retry yourself right now by wrapping your API call in a try/catch and then catch the exception when the HttpStatusCode is 429 (too many requests):

try
{
 await orderService.DeleteAsync(orderId);
}
catch (ShopifyException e) when ((int) e.HttpStatusCode == 429 /*too many requests*/)
{
  // You hit the rate limit. Consider waiting for 10 seconds to clear your rate limit and then try again.
}

I’ve got plans to throw a specific rate limit exception in a future release, but for now the above code is what I use in my own apps. I’d also like to somehow expose the current rate limit after every request, but don’t have any ideas as to what that might look like right now.

1reaction
clement911commented, Aug 15, 2016

One idea comes to mind. For each entity type (Customer, Product, etc…), create a corresponding enum annotated with the flags attribute that enumerates all fields of the entity.

For example

[Flags]
internal enum CustomerFields
{
 None = 0,
 FirstName = 1,
LastName = 2,
OtherField = 4,
etc....
}

Then change each property setter to keep track of what fields were updated (a private property of type CustomerFields would exist on each Customer). Whenever updating the entity, the code would serialize only the fields that were set…

Another idea would be to keep a copy of each loaded entity and then compare it to the entity being posted to find out what changed…

This is similar to how Entity Framework and other ORMs work…

Read more comments on GitHub >

github_iconTop Results From Across the Web

PUT vs POST - Comparing HTTP Methods - KeyCDN Support
PUT is best used when you are updating or replacing existing data on the server, while POST is best used when you are...
Read more >
Difference between PUT and POST in REST APIs
It essentially means that POST request-URI should be of a collection URI. PUT method is idempotent. So if we retry a request multiple...
Read more >
HTTP Request Methods – Get vs Put vs Post Explained ...
In this article, we'll be discussing the get, put, and post HTTP methods. You'll learn what each HTTP method is used for as...
Read more >
PUT vs POST – Difference Between Them
PUT method is called when you have to modify a single resource, while POST method is called when you have to add a...
Read more >
What is the difference between POST and PUT in HTTP?
Overall: Both PUT and POST can be used for creating. You have to ask, "what are you performing the action upon?", to distinguish...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found