WebAPI routing to a function with multiple optional parameters - c#

I have the following function header in WebAPI controller declared as route:
[Route("page/{page}/{cityfilter?}/{statefilter?}/{organizationfilter?}")]
public IEnumerable<Contact> GetContact(int page, string cityfilter = null, string statefilter = null, string organizationfilter = null)
{
...
}
The issue here is that I'd wish that every parameter is optional, so I'd want to make a request that has either a cityfilter, a statefilter, an organization filter, two of them or three of them, and then be processed and router by this function, but I have no clue about how I can build the URI so that, for instance, this route works for just the statefilter.
How can I do that in WebAPI? How should I call the resource address from, for instance, a Jquery Ajax call?
Thank you.

Since the parameters are simple types (strings), they can be bound either from the route data (URL path) or the query string. So you can move the optional parameters to the query string and have your route only match the page parameter.
[Route("page/{page}")]
Here's an example of a URL that you would use to call this action from the browser or from an AJAX call:
www.yourapidomain.com/page/1?cityfilter=aCityFilterString&statefilter=aStateFilterString&organizationfilter=anOrganizationFilter
You may of course omit any of the optional parameters or change their order.
The action method signature can remain as it is in your example.
For more information, you can have a look at parameter binding ASP.NET Web API.

Related

Web API model validation based on paramters

I have API like this , what i want his
When i call with out any parameters
http//localhostcustomer/employes/ it should return
StatusCode(StatusCodes.Status404NotFound)
And if pass with invalid parameters http//localhostcustomer/employes/customerid=abcsd&employedid=yyyy it should return
StatusCode(StatusCodes.Status400BadRequest);
how to do model validation for this
[Route("customer/employes")]
Public class testcontroller : controller
public List<Emplyees> get( [Required]int customerid, [Required]int employedid)
{
if (ModelState.IsValid)
return OK
else
StatusCode(StatusCodes.Status400BadRequest);
}
since you dont actually pas a model u dont realy get to place the validation on something since u just pass loose parameters.
u could create a method or class that validates your input parameters like
IsValid(CustomerId,EmployedId) return Ok()
Or modify the current ModelState.IsValid so
ModelState.IsValid(CustomerId,EmployedId) return Ok()
Since it seems that your are trying to create a Get request with Required parameters it might be interesting to format this as a proper REST call so your path looks like
/customer/[CustomerId]/employes/[employedId]
Without parameters - Not Found
If you haven't registered (either explicitly or implicitly via attributes) the route /employees then ASP.NET will not find a handler for that request that's why by default it will return with 404.
With malformed parameters - Bad Request
That's a bit more tricky. Whenever you have a route with parameters then you can define their types as well. By default if the request contains the route parameter, which can't be converted into that specific type then ASP.NET will not call your handler, rather than it will try to find a better route for that.
Request: /employees/123info
[Route("customer/employees")]
public List<Employee> Get( [Required]int someParameter) {
If you want to catch malformed requests as well, then you have to use string parameters and try to apply the conversion logic manually:
[Route("customer/employees")]
public IActionResult Get(string someParameter) {
bool isItAValidParameter = int.TryParse(someParameter, out var validParameter):
if (!isItAValidParameter)
...
You are not passing a model as your parameter, hence validating a model makes no sense. You need to validate your loose parameters explicitly. In case of invalid parameters:
Request: /employes/customerid=abcsd&employedid=yyyy
By default, the api will return status 400 - Bad Request Error.
In case of without any parameters:
you will get HTTP ERROR 404 - Page can't be found
I would take a look at FluentValidation, which is a popular .NET library for building strongly-typed validation rules.
You can create a custom validator that checks your controller method parameters and that creates a response accordingly.
Something along the lines of:
var validator = new CustomerEmployeeValidator();
var validation = validator.Validate(someparameter);
if (!validation.IsValid)
return BadRequest(validation.Errors);
On how to create the validator, you should look into:
https://fluentvalidation.net/
As a side note, when the input parameter is incorrect, you should always return a BadRequest StatusCode instead of a NotFound StatusCode.
A NotFound StatusCode is typically used when you are searching for specific data externally, and is not found in that repository.

ASP.NET Webapi with parameter

I am building a webapi to getBalance of a customer from Db.It works well and i can retrieve the data.But the problem is in the parameter part.
For eg. In ASP.NET webservice when we request the service it gives us a page where according to the the service we get to enter the parameters in the textbox and upon
firing up the service we get the data.
I have my webapi up and running and the uri looks like this --- http://localhost/api/accounts/balance/cs-001
AccountsController
public class AccountsController : ApiController
{
[HttpGet]
[ActionName("balance")]
public string Getbalance(string accountNumber)
{
var data = BusinessLayer.Api.AccountHolderApi.GetBalance(accountNumber);
return data;
}
}
And my route
RouteTable.Routes.MapHttpRoute("OfficeApi", "api/{controller}/{action}/{accountNumber}");
So the accountNumber is the paramater here.Now one of my mobile app dev friend to is going to use this api suggests me to move account number in parameter rather than url.Like in my case we append the parameter in the url to retrieve the balance from the db.I want to know how to move the account number (the param) from the url to parameter in asp.net webapi.
I am new to webapi.Help needed.Thank You.
All you have to do is change the route since account number is already a parameter on the function:
RouteTable.Routes.MapHttpRoute("OfficeApi", "api/{controller}/{action}");
Then you'll be able to do:
http://localhost/api/accounts/balance?accountNumber=cs-001
#Avitus's answer is correct. Here is some belabor in case you are interested.
If someone requests this URL: http://localhost/api/accounts/balance?accountNumber=cs-001
ASP.NET routing will figure out the controller and action based on your routing configuration. Once the controller and action have been discovered, then it will try to bind the parameters in the action like this:
By default, Web API uses the following rules to bind parameters:
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Here is the full article.

What is the function of [FromBody] Attribute in C#?

Is there a function of [FromBody] attribute? I mean, when I use it for example:
public async Task SetUser([FromBody]User user)
and when I use:
public async Task SetUser(User user)
The server get the same object without problems, so, it's necessary set it, or I can remove it without worries?
Grettings!
User is a complex type, so by default the server will try to resolve it from the request body. If you had a simple type -- e.g.
public async Task SetUser(string userId)
the server would try to resolve the value via URL binding. You can override that behaviour by specifying
public async Task SetUser([FromBody] string userId)
I usually leave [FromBody] in the signature simply for the sake of readability.
There are two ways parameters can be passed to the server - via URI, or as part of the request body.
When data are passed in URI, they become query string - e.g. http://server.com/something?userId=5. This will be handled by the action method with int userId argument.
When data are passed in request body, then you cannot see them in the URI - it would be http://server.com/something, for example. Parameters are then passed as name-value pairs inside the request body.
However, in order to pass anything through body, there must be the body, and GET request normally doesn't have request body (it can have it, technically, but I'm not sure if it's used to pass parameters to GET action methods). You would usually expect arguments to be adorned with the FromBody attribute in POST action methods. Likewise, you would usually expect GET actions to receive arguments through URI, which is more in line with the purpose of the GET method.
You can specify either FromUri or FromBody to control behavior. There is also BindModel attribute which lets you define custom binding.

How to retrieve a suffix from url as an action parameter when performing attribute routing in ASP.NET

Given Attribute Routing in ASP.Net Core (but I guess MVC and WebAPI work the same way), I want to be able to do something like this:
[Route("api/[controller]")]
public class SampleController : Controller {
// GET api/sample/123e4567-e89b-12d3-a456-426655440000/folder/subfolder/file.css
[HttpGet("{id}")] // this is wrong, how should it be written correctly?
public string Get(Guid id, string urlSuffix) {
return null; // return stuff based on the id and the full url
}
}
in the URL taken as example in the comment (api/sample/123e4567-e89b-12d3-a456-426655440000/folder/subfolder/file.css) the SampleController.Get method should be called with the following parameters:
id: 123e4567-e89b-12d3-a456-426655440000
urlSuffix: folder/subfolder/file.css or /folder/subfolder/file.css (I don't really care for the leading /)
If there are additional query parameters, these should be included in the suffix as well.
I thought about using the raw request URL, but I'd still need a way to specify an action which is executed and what ever I thought of was too late, ASP.Net already figured out that there isn't any URL for the given action.
I would like to use controllers for this, instead of adding some "raw" code into the ASP.Net Core execution pipeline.
Update:
This exact example doesn't work for me with asp.net core dotnet core and kestrel service:
[Route("api/[controller]")]
public class SampleController : Controller
{
// GET api/values/5
[HttpGet("{id}/{urlSuffix}")]
public object Get(string id, string urlSuffix)
{
return new {id, urlSuffix};
}
}
When I call http://localhost:5000/api/sample/some-id/folder I get the correct result, but when I call http://localhost:5000/api/sample/some-id/folder/subfolder/file.extension I get a 404 error.
Referencing: Handling a Variable Number of Segments in a URL Pattern
Sometimes you have to handle URL requests that contain a variable
number of URL segments. When you define a route, you can specify that
if a URL has more segments than there are in the pattern, the extra
segments are considered to be part of the last segment. To handle
additional segments in this manner you mark the last parameter with an
asterisk (*). This is referred to as a catch-all parameter. A route
with a catch-all parameter will also match URLs that do not contain
any values for the last parameter.
Your template and placeholders will change to ...
[HttpGet("{id:guid}/{*urlSuffix}")]
Given the following URL ...
"api/sample/123e4567-e89b-12d3-a456-426655440000/folder/subfolder/file.css"
then
id = 123e4567-e89b-12d3-a456-426655440000
urlSuffix = "folder/subfolder/file.css"
Because the / is already part of the template, it will be excluded from the urlSuffix parameter.
the *urlSuffix acts as a catch all for everything after the {id}/ in the URL. If there are additional query parameters, these will also be included in the urlSuffix as well.
You were getting the not found error because your example URL could not find a matching route of api/sample/{id}.
I included the :guid route constraint based on your original example expecting a Guid for id parameter.
If the id is not going to be a Guid always you can remove the constraint and it will work for your updated example.

How to make sure I have a "blank" request for home page in mvc

In MVC there is a whole fancy routing engine, which works very well and directs requests to /home/index (for "empty") requests.
I need to support some horrible legacy code and sometimes I need to do special things when QueryString and/or path is not empty.
How do I know (When I'm in /home/index) that the actual request is:
To the root of the website
Without any kind of parameters?
My attempt:
if (Request.QueryString.HasKeys() || Request.Path.Length > 1)
In your Controller for the given method that is returning the view you want to be redirected to, if there are query string parameters that you need to reference, include them as parameters:
public ActionResult Index(int someValue, string someText)
{
return View();
}
If you need to go to Index whether these query string parameters have been included, either
Make the parameters optional.
Overload the method with no parameters specified.

Categories