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"}
Related
I've been trying to figure this out for about a week now. It's time to ask S.O.
I have 4 overall goals here:
The controller code needs to use ViewModel request inputs for validation reasons. (Controller Snippet)
Client code for my API should use a nice model syntax. (Client Code Snippet)
For the swagger UI page, I would like the "Try me" interface to be usable. Either a bunch of text boxes, or a text area for a json blob to serialize and send over.
GET request
Client Code Snippet:
var response = client.GetUserProductHistory(new Models.UserProductHistoryRequest() {
Locale = "en-US",
UserPuid = "FooBar"
});
Controller Snippet
[HttpGet]
[HasPermission(Permissions.CanViewUserProductHistory)]
public JsonPayload<UserProductHistoryResponse> GetUserProductHistory([FromUri]UserProductHistoryRequest model)
{
JsonPayload<UserProductHistoryResponse> output = new JsonPayload<UserProductHistoryResponse>();
return output;
}
I have tried using [FromBody]. It looks great, but I get an error that says 'GET requests do not support FromBody'.
I tried using [FromUri], but then the generated client gives me like 15 method parameters per call in the generated client.
I tried using [FromUri], and operation filters so that the parameters would be condensed into Ref parameters (complex objects as defined by the spec). This actually worked decently for the client generation and the server side. Problem is, the UI for swagger looks really lame. A single TEXT box that you can't actually use very well. If I can figure out how to get the Swagger UI to change the appearance of the [FromUri] request to more closely match the [FromBody] UI, I will be in good shape here. Any ideas or pre-existing content that would point me in the right direction here?
Swagger is not the limitation - REST itself is. By definition of REST, web servers should ignore the incoming request body on all HTTP GET methods. ASP.NET enforces this convention, which is why you it doesn't allow you to use [FromBody] on the GET method.
When designing a REST API, the better practice is to use POST methods for an actual search. This will allow to use [FromBody], and as a bonus, Swagger will behave the way you want it to. See here for a supporting opinion: https://stackoverflow.com/a/18933902/66101
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 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!
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 :-)