I have a server-side service (using ASP.NET Core) that should provide a REST API to various clients.

Behind the service, I use a SQL server for data storage.

The controller has the following route attribute:

[Route("api/[controller]")]
[ApiController]
public class CustomDataController : ControllerBase
{…}

There are data provider methods:

[HttpGet(nameof(GetDataForGuest))]
public async Task<IQueryable<Specs>> GetDataForGuest(int skip, int take, Guid organizationID) => await dataProvider.GetDataAsync<Specs>(skip, take, organizationID);

There are methods to create or update data on the server:

[HttpPost(nameof(SaveDataToServer))]
public async Task SaveDataToServer([FromBody] List<Specs> specs) => await dataProvider.SaveDataAsync<Specs>(specs, isNewData: true);

According to many articles (like this one), GET is for returning some resource, POST is for creating and PUT is for updating:

  • GET — For returning resources
  • POST — For creating a new resource
  • PUT — For updating a resource
  • PATCH — For updating a resource
  • DELETE — For deleting a resource

However, as you can see, my methods are only facades, the data is saved by the data provider, and I can decide based on a bool variable to insert or update an incoming record.

As you can also guess, I don't use traditional REST URLs (like api/user/1), as I need complex data, so my URLs look like this:

https://192.168.0.101:8080/api/customdata/GetDataForGuest?skip=0&take=10&organizationID=58b77372-abbc-435e-9a5b-d77bc05129c7

Currenty I get data with the HttpPost for both inserting new records and updating existing ones (and it works normally). My concerns are about best practice: should I use HttpPut when I want to update an existing record? According to the specifications, using PUT is idempontent by definition while POST is not, so executing the query should not depend on how many times it was called.

On the client side, I use some query building (based on parameters) and I call the method with a POST action:

public async Task SaveDataToServer(string methodName, List<T> data, bool isNewData)
{
  var parameters = GetParameters(methodName, isNewData);
  var baseUri = new Uri(baseUriString, methodName);
  var targetUri = UriBuilding(baseUri, parameters).AbsoluteUri;
  var result = await httpClient.PostAsync<List<T>>(targetUri, data);
}

So using only POST is considered as best practice? Or should I separate the update actions to another method marked with HttpPut? Does it matter considering that the logic behind the methods decides what will happen and not the verb?

  • are you providing users with a client implementation for your api? if not, why not? – Ewan Nov 22 at 13:35
  • Yes, I implement a client side as well, and I use Post methods in the HttpClient. I'll add some client side code to the initial question. – Nestor Nov 22 at 13:38
  • 2
    It sounds like you just want to call it REST without following the description of REST. – Rob Nov 22 at 15:03
  • 2
    @Nestor because looks like you don't totally understand what REST is. So how can you custom something that is strange to you? – Laiv Nov 22 at 15:57
  • 2
    On the other hand. If we deliberately ignore the HTTP semantics and how they conceal with the so-called REST principles, why to use HTTP at all? That httpClient you use definitively "speak" HTTP but you are using it to speak what-ever-your-protocols-is. It's like hiring a spanish-speaking to make it speak chinese. – Laiv Nov 22 at 16:08

According to this great tutorial, there are several problems with my initial approach.

  • Good: /users/12345
  • Poor: /api?type=user&id=23
  • POST Create
  • GET Read
  • PUT Update/Replace
  • PATCH Update/Modify
  • DELETE Delete

So it looks I have to redesign my customized RESTless API but following these suggestions, it will be much more REST-like.

  • 1
    As a matter of fact I was just going to write about the resource naming and at the same time your answer appeared - I see you're on the right track now ;-] – t3chb0t Nov 22 at 18:27
  • 1
    @t3chb0t Nice to see some supporting comment :) Yeah, I guess, it is time to reconsider the phrase: think before act… :P – Nestor Nov 22 at 18:29

If you provide a client for your api it doesnt matter.

"RESTfullness" implies to most people that you map the different verbs to your operations.

However, as you note its not always the most practical approach and the assigment is somewhat arbitrary.

But the only reason to use the verbs is so that the consumers of the API can create their own clients by following the conventions as they see them.

If you simply provide a prewritten client for the consumers to use, then they will never see the underlying use of the protocol. You can use POST or even DELETE for everything if you like.

Furthermore. the "specifications" for REST are super vague. The idea is that simply by browsing the API a user would be able to figure out the operations. However in practice this isnt really possible. For APIs of any complexity the user will consult and follow the documentation.

At which point you can simply say "for this operation use POST" the user will not be confused, their client will not break, they will just "tsk" if they disagree and type "POST"

According to many articles (like this one), GET is for returning some resource, POST is for creating and PUT is for updating:

Yes, there are a lot of articles that say that.

There is a consistency problem in that position. The world wide web depends heavily on HTTP. The most familiar HTTP clients are web browsers; applications where the primary use case is rendering HTML. And HTML only includes native support for GET and POST.

So when you ask:

only POST is considered as best practice?

My answer: it has worked in web browsers for more than 20 years.

If you want to understand what HTTP Methods mean, then you need to read the appropriate specification. For the "standard" methods, that means section 4.3 of RFC 7231. For other methods, you can look them up in the HTTP method registry, and follow the links provided there to the authoritative sources.

HTTP methods don't describe what the server does, but instead what generic components are allowed to assume; they constrain the semantics of the requests, not the implementations.

  • Shouldn't if be pointed out that it's completely valid to create with PUT. This is such a common misunderstanding i.e. equating the HTTP verbs to CRUD. – JimmyJames Dec 3 at 19:31

Maybe use simpler RPC style API instead? Where everything is a POSTed as JSON. Some companies have shifted using that instead of REST. Like Dropbox API v2 https://www.dropbox.com/developers/documentation/http/documentation

Our company also has moved from REST to RPC style APIs and it's awesome.

Edit: In our API binary download is still using GET and binary upload is still using normal HTTP form file upload.

There are a few things here that I think are (common) misunderstandings of HTTP semantics and/or REST. As noted in answers/comments, REST and HTTP are distinct but the concepts of REST were derived from how the web and HTTP work (and to a much lesser extent, what didn't work so well) so the distinction isn't that important, just something to file away in the back of the mind.

PUT — For updating a resource

This is probably the most common misconception around HTTP verbs. Yes, PUT can and is used to update resources. It can also, however, be used to create resources. The only requirement is that it be idempotent. In simple terms it's a write. Whether there was something at a given URI before you do a PUT on it is irrelevant. The only thing that matters is what it looks like after you do write. POST also creates resources but the distinction it and PUT (from a REST perspective) is that with POST, the server controls what the URI is for that resource. With 'PUT' the client controls that. POST is also non-idempotent. It's a one-shot deal which is why you sometimes get errors in a browser saying that you are attempting to resend a POST request. Essentially that are saying you may be creating a second resource which could be bad if the created resource represents a purchase, for example. Whether you need PUT or POST create semantics (or both) depends on your specific needs.

At a basic level, if you need to centralize the control of how URIs get created (e.g. prevent URI collisions between clients) and enforce 'only-once' rules, you should probably be using POST. The other common situation where POST is a good fit is when you have a asynchronous requirement. You have the client issue a POST and return the URI where the results will eventually be available. That way the the client gets an immediate confirmation that the request was received and has a reference it can use for asking the server about that request. Often resources generated in this way are only available for a fixed period of time.

As you can also guess, I don't use traditional REST URLs (like api/user/1), as I need complex data, so my URLs look like this: ...

This is another common misconception. REST URIs don't need to look like anything in particular. A lot of people will argue that a human readable URI is not really RESTful at all. There are pretty arguments for why that is the case but I would say that it's just not required. It can be very useful to have human readable URIs but you should not feel constrained. Whether/when using query params is a good practice is debatable but there's nothing inherently non-RESTful about that. The semantics of REST can still be followed. It's more about expectations.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.