I have Users collection with User's. User - Id, FName, LName.
For insert, I pass User instance with Id, FName, LName. The service inserts the User if it's not present.
For update, I again pass User instance with all parameters. The service updates FName and LName for the User with the given Id.
I want to have insert and update as 2 separate methods in the service.
I can't have same URI's for both methods. Something like below:
[WebInvoke(UriTemplate = "Users", Method = "PUT")]
void UpdateUser(User instance);
[WebInvoke(UriTemplate = "Users", Method = "PUT")]
void AddUser(User instance);
Which is the best way to acheive this?
I don't agree with one post saying update URI to have something like :
[WebInvoke(UriTemplate = "Users/{userId}", Method = "PUT")]
void UpdateUser(string userId, User instance);
Because, user id is already present in the Userinstance.
Though I agree that PUT method can perform insert and update. For some reason I need to keep them separate.
PUT, being an idempotent operation should create a new resource at the URL you are PUTting to or completely replace the existing resource (PUT this resource here). If you perform the same PUT to the same URL multiple times you will still end up with the same resource.
POST is not necessarily idempotent, because you are changing an existing resource. If your POST is doing something like adding items to an order and you POST the same data to the same url mutliple times you could end up with an order containing multiples of the same item.
Short answer, make your insert a PUT operation and the update a POST.
I think this SO Answer does a really good job of explaining it.
You can look at it a couple of ways
PUT - for updates, but if provided id doesn't exist then insert
POST - for
creates, insert as many times as it
likes even if you pass the same data
in multiple times as it ignores the ids
So I'd say:
Add user : /Users with POST (however you could do this with a PUT instead theoretically).
Update user : /Users/{userId} with PUT
As it would then be to get the user info, /Users/{userId} with GET ... or to delete the user, /Users/{userId} with Delete.
Not sure if this helps or not, if not then let me know and I'll try again :-)
Related
If I need to update many detail rows, my Web API controller method might look like this, using an RPC style:
[Route("api/updateAccountDetailStatus")]
[HttpGet]
public IHttpActionResult UpdateAccountDetailStatus(int accountId, string status)
That would change all of the detail rows, associated with that account, to the new status.
In trying to take a RESTful approach, my guess is that it would be something like this:
PATCH /accounts/110
{
"status": "hold"
}
[Route("api/accounts/id")]
[HttpGet]
public IHttpActionResult Account(Account account)
What I don't like about that is now the API controller method needs to interrogate the object to see how to handle it. In this case, it would be changing all of the detail rows to that new status. But what if someone calls that patch and sends a different property to modify? Now I have to change behavior based on that? Is there a better way?
I see the conundrum. On one hand, you would like to stay true and not have action names (change, update, etc) in your URI, and on the other hand, this is a special procedure and not quite a PATCH really.
So, for this article I did some work to allow the action to be defined by the type of the message sent, even created a means of doing that in Web API.
The sample code for this is here.
Essentially you expose these as POST or PUT (depending on whether they are idempotent or not) and the resource will have multiple POST or PUT against it. For Example:
GET /api/InventoryItem [gets all items]
GET /api/InventoryItem/{id} [gets detail of a single item]
POST /api/InventoryItem [creates an item]
POST /api/InventoryItem/{id}* [checks in stock items to the inventory]
POST /api/InventoryItem/{id}* [removes stock items from the inventory]
PUT /api/InventoryItem/{id} [renames an item]
DELETE /api/InventoryItem/{id} [de-activates an item]
This is the only solution I have had so far for these types of resources.
UDPATE
Essential you would expose this as PUT (since I imagine it is idempotent) at api/accounts/id sending a payload signifying type of the message:
PUT api/accounts/id
{"detailBatchStateChange": "hold"}
Currently I have the following standard functions in each of my controllers to handle basic CRUD operations:
GET /api/todo Get all to-do items
GET /api/todo/{id} Get an item by ID
POST /api/todo Add a new item
PUT /api/todo/{id} Update an existing item
DELETE /api/todo/{id} Delete an item
However, the time came where I realized I actually need to pass multiple parameters to get a list of todo items that is filtered at the database level rather than retrieving all of the items and using linq.
For example here is how I decided to go about it:
In my Controller:
// POST: api/todo
[HttpPost]
public IList<TodoItem> Get([FromBody]GetTodoItemsRequest request)
{
return _todoItemManager.GetTodoItems(request.Name, request.CategoryId);
}
As you can see I created a new Model called GetTodoItemsRequest which will have a property for each of my parameters. In this case: Name, CategoryId.
I figured when dealing with multiple parameters and retrieving a list it is best to do POST and create a model specifically for it. Rather than using a GET and passing all kinds of information in the url.
It seems a bit strange to be doing the above... Would msot see it as a perfectly fine solution or is there something I am missing in the WebAPI world?
I believe that is semantically incorrect to use POST method for a simple read operation, even if you need a complex model. You are doing a pure query on your resource called todo, and this should really be a GET operation for many reasons:
It should be cachable: POST request aren't cachable by their nature, and caching is an important constraint in RESTful services.
It should semantically indicate that no side-effect will be raised from the call: GET requests must be idempotent and safe, POST operations, instead, indicate some kind of data manipulation. Your operation (filtering) is both idempotent and safe, so it should be spontaneously represented by a GET request.
The part of the URI after a ? character is called query string for a reason: it represent parameters that further specify the scope of a request. Well, isn't filtering results just an example of this approach?
Apart from that, it seems to me that, if Name and CategoryId are required parameters for your query, your filtering operation could be better represented by another URI in which Name and CategoryId are turned into route parameters:
http://yourhost.com/api/users/{name}/categories/{categoryId}/todos
Assuming a relationship between your name parameter (a user name maybe?) and the categories.
If, instead, your parameters are completely optional, then leaving them as query string parameters is the best choice:
http://yourhost.com/api/todos?name=nameValue&categoryId=categoryIdValue
A side note:
you should really use plural for your resources if they represents a collection of items: e.g. api/todo will return an array of todos, so you should rename it into api/todos.
I'm currently in the process of designing as RESTful of an API as I can using Microsoft's Web API 2 in C#. What I'm struggling on is how best to represent resources or the proper way to do it where the GET call and POST/PUT are very different.
For example say I have something calls states that have an id, name, status, etc., these can be assigned to a document. So I have a route like this /documents/{id}/states/ . If I call a GET here I need to get the full list of all assigned states including their id, name, etc.
However, in order to change which states are assigned to the document I simply need to pass the id. I cannot do this individually, it must be an array that gets sent up since users may be interacting with hundreds or thousands at a time.
So in this case I have a few issues. I don't even know if POST or PUT is correct here, and second whichever one it is can I just take in an array of integers?
In your case, I would suggest PUT is the method you would be wanting to use, as you know the location of the resource that you are updating. For more info, see here: http://restcookbook.com/HTTP%20Methods/put-vs-post/
In ASP.NET Web API 2 you can use the [FromBody] parameter attribute, so that your method signature would be:
public void UpdateStates(int id, [FromBody]List<int> states) {}
More info on parameter attributes can be found here: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
I've some code like the following;
We're going to create a Note but we may know the CustomerId when we do so I've two URLs;
public ActionResult CreateByCustomer(int id)
{
Session["ncAppointmentId"] = 0;
Session["ncNoteDate"] = null;
SetConsultant(0);
return RedirectToAction("Create", "Note", new { id = id });
}
public ActionResult Create(int id = 0)
{
int _CustomerId = id == 0 ? Convert.ToInt32(Session["CustomerId"]) : id;
Session["TemplateIds"] = null;
and so on.......
ViewBag.CustomerId = _CustomerId;
When I look at the performance in Firebug the RedirectToAction causes a GET with a '302 Found' status which can incur up to 1 second's delay.
If I change the RedirectToAction line to
return Create(0);
Then the GET doesn't happen and performance is improved.
But I'm looking for opinions on what the downside is or what I've missed ?
The RedirectToAction result tells the client to request a new page, so it will naturally incur overheads since the client is now having to make a second request to your server. Sometimes this is necessary, sometimes - as in your example - not.
Generally I use RedirectToAction to return the client to a specific page after performing an action such as creating a note. This can be useful if you're on a page that lists notes and you want to refresh the page after creating a new one. The end result is that the page is refreshed, and the Create action does not appear in the user's browser history.
If your Create method returns a View, there can be some interesting side effects of calling it directly. In general the MVC code will handle it, but you can get some weird results - like the client's URL being different to what you expect in the subsequent requests, etc. If you're OK with this, fine.
Another option would be to get rid of the CreateByCustomer action and simply call the Create view with a parameter - named customerID for instance. This gives you the same ability to call it different ways without having to have multiple entry points. The client's location would reflect (in the query string) the difference between Create and Create?customerId=12345 which may or may not be what you're after.
<opinion>
Some Style Notes:
If you're storing lots of session data, create a class to hold it instead of creating lots of entries in Session[].
It's not particularly difficult to use jQueryUI to create an in-page editor for your notes rather than defining a view - check out this example. More elegant too :P
</opinion>
The RedirectToAction method is going to return an HTTP response that has a Found status code and a Location URL pointing to the place you are redirecting the client. The cost is simply another GET request, which I would not consider expensive. The decision to redirect or not should be made based on whether it conceptually makes sense, not based on whether you are making one less GET request.
I don't entirely understand the motivation here, however. If you elaborate on why you are trying to redirect, maybe I can help you choose a pattern that makes more sense.
Typically, you would not name a method Create* in an HTTP API. The idiomatic, correct prefix is Post* or Put*, depending on whether you are adding a new resource (but naming it) or creating/replacing a resource (and naming it), respectively.
The big difference is regarding if you want the url to change to the "Create" one. If it's ok to show whatever you are showing with that url, then avoid the redirect. Redirect is useful when you have an old url and you want it to point to a new one and also in the situation when you want to avoid saving new stuff if the user refresh de page (as it will refresh only the redirect request and not the post).
I have a Web API application and I'm using the below url for both bulk (tens or hundreds) inserts and updates which return just OK or Failed.
POST api/v1/products
which is mapped to my action:
public HttpResponseMessage PostProducts(PostProductsRequest request)
{
...
}
PostProductsRequest object contains a Products property of type List.
If the Id property exists for a Property, I update it otherwise it'd indicate an insert.
But I'm just wondering whether I should use Post for Bulk Inserts only and Put for Bulk Updates, not sure. What's the best practice and advantage of each approach?
How to Design a Restful API for Bulk Inserts and Updates?
Either method can be used, depending on your requisites, but this doesn't mean they don't have significant differences. HTTP methods are not CRUD. PUT or POST are not Create and Update, or the other way around.
PUT completely replaces the resource at the given URI with the entity provided, so it can be used to create and also to update, but only if it contains the full representation. A GET request made immediately after a PUT should return the same resource. The representation may be exactly the same, although it's possible for the service to add default values that were missing from the PUT'ed representation.
POST tells the server that the entity being provided is subordinated to the resource at the given URI, and they have an agreement on what it should be done with that. It might be anything, a create, an update, any operation that isn't standardized by HTTP itself.
With this in mind, a bulk insert or update with PUT is only RESTful if you're replacing the whole collection identified by the URI. This doesn't have to be necessarily your whole collection associated with that media type. The URI can have a querystring slicing the dataset, and you perform the bulk operation on that slice only.
For instance, if you have the following collection resource:
GET /api/products
Represented by:
{'products': [product1, product2, product3]}
And you want to add three more products, a bulk operation with PUT would have to append your new products to the existent and send the whole collection back:
PUT /api/products
{'products': [product1, product2, product3, product4, product5, product6]}
However, if you have a filter constraint you can apply to /api/products that would return an empty collection on the GET above, then it would be fine to do the PUT only with the new products to that filtered resource. For instance, let's say the products above can be filtered by a partner attribute, they have partner x and you're adding for partner y:
In that case, it's fine for you to do:
PUT /api/products?partner=y
{'products': [product4, product5, product6]}
And a GET /api/products after that returns:
{'products': [product1, product2, product3, product4, product5, product6]}
As long as GET /api/products?partner=x returns:
{'products': [product1, product2, product3]}
And GET /api/products?partner=y returns:
{'products': [product4, product5, product6]}
This might seem complicated and sometimes it looks like it's better to use POST instead of PUT, but keep in mind that the whole operation above is standardized. It's using PUT exactly as it's intended to be used. The operations can be more straightforward with POST, but they are not standardized and you'll have to design and document your own syntax for it.
I would recommend to use POST to create and PUT to update (actually create or update as it is indempotent).
From the RESTful Webservices Cookbook (O'Reilly):
Use POST and a collection resource to create a number of similar resources at once.
Let
clients include information about the resources to be created in the request. Assign a
URI for all the resources created, and redirect the client to the collection using response
code 303 (See Other). A representation of this resource includes links to all the newly
created resources.
To update or delete a number of similar resources in bulk, use a single URI that can
return a representation containing information about all those resources. Submit a
PUT request to that URI with information about the resources to be updated or a
DELETE request to delete those resources.
In all these cases, ensure that the processing of the request is atomic.
I just happened to be looking at the HTTP 1.1 method definition and was reminded of this question.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
This would indicate to me that if you were to use PUT and the payload contained a not existing resource with enough information to create it then it should be created and therefore PUT would be the correct method verb in a bulk operation that can create & update resources.
The most "standards compliant" way for batch operations in RESTful Web services is to use one of the various 'collection' approaches (i.e. DELETE /mail?&id=0&id=1&id=2) or you could use a batching handler to simply the process.
Honestly, I use the exact same pattern as you do except for the fact that I use POST for object creation and PUT for updates only (which is the standard way of doing it). Also POST should return 201 - Created along with the created object and PUT should return 204 - No Content with no data, if the operation succeeds. Of course, as you're doing bulk creation, you may opt not to return the array of newly created objects with POST.
To summarize it:
POST api/products
|
|---> Success: 201 [NewObject1, NewObject2, ...]
|---> Failure: Relevant error code as to why the operation failed
PUT api/products
|
|---> Success: 204
|---> Failure: Relevant error code as to why the operation failed
Update: vNext of ASP.NET Web API will have batching built in!