I want to use the FromHeader attribute with multiple names (e.g. for multi-language support) to the same model.
public async Task<IActionResult> Create([FromHeader(Name="Test-NLD"),FromHeader(Name="Test-ENG")] string Test)
You can not use multiple FromHeader attributes with another name to the same model (error CS0579, Duplicate 'FromHeader' attribute).
What is the best way to resolve this? I use Swagger, so it would be nice if the Swagger documentation is still correct.
In the short term, you'll need to make two different parameters on your action, one binding to each possible Header key.
public async Task<IActionResult> Create(
[FromHeader(Name="Test-NLD")] string testNld,
[FromHeader(Name="Test-ENG")] string testEng)
FromHeader doesn't accept multiple entries on a single parameter, or wildcards.
For now, if you're only going to support 2 languages, you might be able to squeak by with this solution. It's, understandably, very maintenance intensive and the definition of "Technical Debt".
ASP.Net and Swagger both work their best when the Header Keys are static. It's not possible to document "You need supply a header called Test-*, where * is 'NLD' or 'ENG'."
In the future, I still very much recommend that you split the choice of language into its own key. You could even use the "standard" Accept-Language header.
public async Task<IActionResult> Create(
[FromHeader(Name="Accept-Language")] string clientLanguages,
[FromHeader(Name="Test")] string test)
Accept-Languages is written so that it's expected the client gives you a CSV of languages they want, in priority order. You select the first one that you support, and inform the client of the selection with the Content-Language response header.
There are also more elegant methods to get the Accept-Language header, see this post.
Related
I'm pretty new to web development and swagger in general so apologies if the question is too naive.
I'll use the Asp.Net Core Web Api template in visual studio 2019 to better explain my problem, so please consider that environment in the case I have omitted some information (or just ask for the missing part that I'll bring it).
There we have the WeatherForecastController class with a simple GET (which I included the names parameter):
[HttpGet]
public IEnumerable<WeatherForecast> Get(IEnumerable<string> names)
{
//...
}
When I try to test this request using the Swagger page, it doesn't recognize the name parameter.
I have ran other tests to find out what's going on and I found the following:
Swagger does work with IEnumerable as it works pretty well if the parameter is of type IEnumerable<IFormFile> (it displays a list of uploaded files, shows the file selection dialog, etc);
I tried encapsulating the parameter(s) in a DTO class and it seems to break even more stuff (even the IEnumerable<IFormFile> doesn't seem to work inside a DTO class; it only works if passed directly in the parameter list of the [HttpVerb] method;
I tried with other similar types as well as ICollection<string>, List<string>, string[]; none of them seem to work;
This same problem happens using primitive types like bool, int, etc as type arguments to IEnumerable<T>;
So what is happening? Should I set some sort of configuration value so it can work with collections of primitive types?
Update with images showing the problem:
... Get(string names, IEnumerable<IFormFile> file):
... Get(IEnumerable<string> names, IFormFile file):
As you can see, when any parameter in the param list is of IEnumerable the swagger UI doesn't properly show the requested fields like in the second image.
You must specify model binding sources in your case. Your action method should be like this:
[HttpPost("test/names")]
public IEnumerable<string> PostNames([FromQuery]IEnumerable<string> names, IEnumerable<IFormFile> files)
{
//...some code
return names;
}
You shouldn't use two or more complex type parameter as an action parameters until you specify the source they are binding from. That's because complex objects are bound to request body by default and only one parameter can be bound to request body.
As of microsoft docs :
Don't apply [FromBody] to more than one parameter per action method. Once the request stream is read by an input formatter, it's no longer available to be read again for binding other [FromBody] parameters.
Complex type means class variables, arrays, and those which are not primary types like int,double, string and etc.
Swagger generates this UI for action above:
As the final word , Note that you can't send something in your request body when you are using GET http request.
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
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