Creating WebApi controller with different verbs - c#

I have WebApi controller that receives the request Get all entries:
public IEnumerable <client> GetAllClients ();
I need this same controller method:
public IEnumerable <clients> GetMaxPaymentClient ();
This method also available on request Get, how do I describe this method and how to call the client page?

It won't work having two methods with the same method signature and same HTTP verb at the start of their name in the same controller if you just use the default Web API routing. This operates by convention based on method names and there's no way for it to tell which method you intend to reach using a GET request to your controller.
Ways of differentiating the two methods could be:
Add a [route] attribute to one or both of your methods to differentiate them. More detail on this is at http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2. E.G. you could add [Route("clients/maxpayment")] to your second method.
Move one of the methods to a different controller. This is what I'd recommend if you're writing a REST style API. Max payment sounds like it should be a different resource from just general details for clients
Amend the method signature of the methods so that they're different, such as by adding an ID argument to one of them. Web API will be able to handle separate routes for methods like Get() and Get(string id). Presumably this isn't an option for you as I assume you have your desired method signatures already.

Related

Asp.Net Web Api : Handling two get request with same signature

Current situation
[HttpGet]
public HttpResponseMessage CheckProfileStatus(int id)
{
//check profile status from third party
return //profile status and HttpStatus;
}
[HttpGet]
public HttpResponseMessage ProcessTheProfile(int profileId)
{
//check profile status from third party again, if status is "Good", do something
return //someMessage and HttpStatus;
}
Url to call CheckProfileStatus method
myUrl/api/Profile/123
Url to call ProcessTheProfile method
myUrl/api/Profile/?profileId=123
I hope the comments in the code makes the situation clear.
I don't really like the fact that I have two HttpGet methods with same signature in one controller (even though it works).
I don't know if it is best practice.
Question:
Should I extract these two methods in two seperate controllers, is it completely fine to have two HttpGet methods with same signature in one controller or is there better way to handle this situation?
First, this is confusing to any client of the API.
You have 2 GET methods which are virtually identical. In fact most people prefer to add a route covering your first option which basically sets the profileId to the value after the forward slash.
Second, the expectation is that when you issue a GET against an entity with an ID, you get the representation of that entity. This is not what's happening here.
You need to decide which kind of API do you want. A generic one where stuff like this is fine :
myUrl/api/profile/process/123 or process/profile, or whatever else makes sense to the API.
If your API is supposed to be RESTful ( which basically means you will have one method per HTTP verb and nothing more ) then you need to add a new controller for ProcessProfile and then your url can look like this :
myUrl/api/ProcessProfile/123
None of these options is set in stone, they are just that, options.
The comments to the OP thread give some good options as well, which should be considered.

Match controller method by GET parameter

I have a controller marked with [Route("api/entities")]. There is a method for getting all entities:
[Audit(ActionType.EntityList)] // custom annotation for audit
[Authorize]
[HttpGet]
public IActionResult GetEntities()
{
// ...
}
As you can see, by using annotations I save the request to some audit and authorize the request only to allowed users.
Now, I want to enhance this endpoint so it can return the top N entities. Example request: /api/entities?top=5. I have found that I should use an optional parameter for the method and use if to detect the case.
However, I need to save such call in audit as differnt type (e.g. [Audit(ActionType.EntityTop)]) and I do not need an authorization there (everyone can fetch the top entities).
How can I map the /api/entities request to one method and /api/entities?top=N to another? In Spring from Java I would use a params field of #RequestMapping.
I do not want to change the URL of this endpoint, because the top parameter modifies only the list that is being returned so one should use GET parameters for that. By having the same URL I also do not change the semantic meaning of the response (it is still list of the same entities). It is important when using RESTful frontend framework like Restangular.
Are you sure you need same action? It looks like you want to solve different tasks here, so I'd suggest you to create another action
[Audit(ActionType.EntityTop)]
[HttpGet("top/{top:int}")] //you can specify route via Annotations
public IActionResult GetEntities(int top)
{
// ...
}
Request with top entities will match /api/entities/top/5

Syntactic sugar in Web Api - how to omit [FromBody]

Is it possible to omit writing [FromBody]?
[HttpPost]
public string SomeMethod([FromBody]SomeModel model)
{
return "OK";
}
Should I apply some global attribute? How to achieve this?
The model binding attributes in MVC 5 specify a "BindingSource" for each parameter on an action and can specify for each property on a controller, too. You can see specifically the code where it picks it up for the FromBody attribute in the BodyModelBinder
Let me say first that you should beware that as of beta6 (and I think some point in beta5), having multiple parameters/properties with BindingSource.Body is not allowed, and you will not be able to use your actions if you have multiple parameters left as the default. This might actually be desirable if you want to annotate more; my guess is that you do not.
You can see in the DefaultApiDescriptionProvider where the default source is set; unfortunately, there is no hook at this point to tie into, and the method itself is private and not virtual; you'll need to re-implement the entire class.
Once you have, though, it's rather easy to register it in your ConfigureServices to use it instead:
services.TryAdd(ServiceDescriptor.Transient<IApiDescriptionProvider, YourApiDescriptionProvider>());
You can see the default being registered in the source, too.
probably more than one way to achieve this.. here is one way: a custom ActionValueBinder..
Write a custom class to extend from DefaultActionValueBinder.
public class FromBodyParameterBindingActionValueBinder : DefaultActionValueBinder
Override the GetParameterBinding method.
protected override HttpParameterBinding
GetParameterBinding(HttpParameterDescriptor parameter)
{
return parameter.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Post)
|| parameter.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Put) ?
parameter.BindWithAttribute(new FromBodyAttribute()) :
base.GetParameterBinding(parameter);
}
basically what we're saying is that if the Web API system gets a POST or PUT call, make FromBody as the default parameter binding methodology.
Once we have our custom binder, register it globally.
GlobalConfiguration.Services.Replace(typeof(IActionValueBinder), new FromBodyParameterBindingActionValueBinder());
what this does is that for every POST/PUT request, Web API will try to create the model from the request Body. you don't need to provide the [FromBody] attribute explicitly.
having said that, you need to understand the implications of what you're requesting..
Web API has some default rules on model binding. (can be changed)
primitive action parameters are first, looked into the query string
then request body.
complex action parameters are first, looked into the request body.
then query string.
also, web api can only read the first [FromBody] action parameter.. subsequent parameters cannot be read from the body.
the reason i am enumerating the above rules, is so that you're aware of the behavior when you expect some model to be created, but web api does not seem to create it from the body.
for me, explicitly providing [FromBody] is a lot more readable and predictable.. but if you're in full control of your APIs in terms of Http Verbs and expect all requests to be via the Body, it is a perfectly valid thing to do.

Understanding [HttpPost], [HttpGet] and Complex Actionmethod parameters in MVC

I am very very new to MVC the design-pattern and also the Framework. I am also not extremely well- versed in fundamentals of ASP.NET Forms. However, I do understand the basics of web development and HTTP Post and GET as well.
Now, I have been going through some MVC tutorials and I though I was getting a good hold of how MVC the pattern works and also how "Routing Engine" works. Then suddenly I came across a code which looks like folloing:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return Content("Thanks", "text/html");
}
}
I have few questions looking at it:
My understanding of routing engine was that the control is passed to a particular ActionMethod based on URL and normally URL are basically Controller/ActionMethod/Id kind where paramter to action menthod are rather Primitive types. In this example above what kind of URL would it take to call "
public ActionResult Index(MyViewModel model)?"
Since NyViewModel is a complex type, you can not pass it as part of URL. How can you call it?
Why is this second method adorned with [HttpPost] when the first method does not require any attributes? Are there any guidelines on when to use [Http] attributes and when not?
I think I am missing a big pice in the puzzle and both the questions are interrelated. However, need some help in making sense with the relationship
The [HttpPost] attribute tells the routing engine to send any POST requests to that action method to the one method over the other. This is a type of overloading.
Why is this second method adorned with [HttpPost] when the first method does not require any attributes?
The default for a method is [HttpGet]. Because of that, no attribute is needed.
Are there any guidelines on when to use [Http] attributes and when not?
Ideally, attributes should be on every method, in order to avoid confusion. As you get more familiar with how things are working, you will often take shortcuts (as with everything else), and omit them when you know that they are not necessary.
Since MyViewModel is a complex type, you can not pass it as part of URL. How can you call it?
The data will be turned into the model from the data in the body of the request. This can come either as a JSON object, or as Form data. (There are tricks to get the object initialized from the URL, but they can be a little complicated and advanced.)
Generally, complex objects are passed in the HTTP body with verbs that support it such as POST and PUT. The body content must pass Model Binding validation. That basically means that if it's a POST request with Content-Type: application/json, it must deserialize from JSON into MyViewModel. If the content is XML, it must deserialize as XML.
General convention is to have all the primitive types that can be found in the URL path, query, and headers first, then one complex type from the POST (or PUT) body after that. I believe it's possible to put complex types elsewhere, but then you're getting into type converters and custom attributes which you should probably hold off on if you're a beginner.
Why is this second method adorned with [HttpPost] when the first method does not require any attributes? Are there any guidelines on when to use [Http] attributes and when not?
"[HttpPost]" is telling the routing engine that this method overload is only available via HTTP POST. Attempting to PUT /home/index with a body will fail with 404 Not Found, in this case. The parameter-free version of Index() doesn't require it because it can work with any HTTP verb, including GET, POST, and PUT.
Best Practice - Request handling
It is best practice to only use public methods in your controller which are going to be serviced either with a view or with json. For all public methods in your controller, it is best practice to either mark them with an [HttpGet] or an [HttpPost], or one of the other types which I wont cover as they are more edge case scenario.
These Http attributes restrict the method to only servicing those specific types of requests. While the default is [HttpGet], I have found that not marking [HttpGet] in all scenarios can at times lead to unexpected behavior when there are naming conflicts.
Best Practice - PRG
Post-Redirect-Get is a design pattern which essentially stipulates that any time you are going to be sending a response which came from a POST request, you should redirect to a get in order to send the response. This protects from a number of scenarios, including not posting again if the back button is used.
The redirect usually comes in the form of the [HttpPost] ActionResult using return RedirectToAction("MyHttpGetAction");.
Posting complex models
There are multiple ways which you can send a complex model. The main difference is that if you are using a GET request it is in the url, and if you are using a POST request it is in the request headers. If you use ajax then the difference becomes blurred as you will almost always send it in the body.

Is the Web API routing engine smart enough so that identical routing attributes can be applied to different methods?

Consider a method with this routing attribute:
[Route("api/Deliveries/{ID:int}/{CountToFetch:int}")]
The method with these routing attributes is called by the client passing a URI like this:
http://localhost:28642/api/deliveries/42/100
So this way, the path info ("api/Deliveries") and the args ({ID:int}/{CountToFetch:int}) appear to be homogeneous and/or homologous.
The method on the Controller that is called gets these args assigned to it, as this makes clear:
public IEnumerable<Delivery> GetBatchOfDeliveriesByStartingID(int ID, int CountToFetch)
{
return _deliveryRepository.GetRange(ID, CountToFetch);
}
This works fine.
BUT there is another, perhaps more "accepted" way of forming routing attributes, where args are excluded:
[Route("api/Deliveries")]
...wherewith GetBatchOfDeliveriesByStartingID() is now called this way:
http://localhost:28642/api/deliveries?ID=42&CountToFetch=100
This is fine (maybe); what gives me pause is, now that the routing attribute has been simplified so, how does the routing engine know the difference between GetBatchOfDeliveriesByStartingID(), and GetDeliveryById(), for instance, which currently has this routing attribute:
[Route("api/Deliveries/{ID:int}")]
...but under the new regime would have:
[Route("api/Deliveries")]
...and be called like so:
http://localhost:28642/api/deliveries?ID=42
IOW, the two methods have the exact same routing attribute applied to them.
Is the Web API routing mechanism smart enough to still invoke the appropriate method based on the args passed in the URI and/or based on the return type of the two methods being different (e.g., the former returns an IEnumerable of Delivery, whereas the latter returns a single Delivery)?
Yes, the Web API routing engine is, if not as smart as Einstein, at least as smart as your average Congressman
This code:
[Route("api/Deliveries")]
public Delivery GetDeliveryById(int ID)
{
return _deliveryRepository.GetById(ID);
}
[Route("api/Deliveries")]
public IEnumerable<Delivery> GetBatchOfDeliveriesByStartingID(int ID, int CountToFetch)
{
return _deliveryRepository.GetRange(ID, CountToFetch);
}
...works fine.
If I pass this:
http://localhost:28642/api/deliveries?ID=7
...GetDeliveryById() is called.
...and if I pass this:
http://localhost:28642/api/deliveries?ID=7&CountToFetch=42
...GetBatchOfDeliveriesByStartingID() is called.

Categories

Resources