ASP.NET Webapi with parameter - c#

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.

Related

Read action parameters from JSON properties

How can I get the parameters of an ASP.NET Core MVC controller action from the properties of a JSON object that was sent in the HTTP request body?
If the request body contains form data and the content type is appropriate, the action method parameters are populated automatically as expected.
But nothing works if the POST data format is JSON and the content type is "application/json". I think this is quite common for API requests? I tried adding the [FromBody] attribute to all parameters, but the documentation says I can only apply that once. Well, I can write it multiple times and nobody complains, but neither helps. Even if I want to bind all the JSON to a single string parameter, it remains null.
Can ASP.NET Core actually handle JSON POST data? In the usual parameter binding comfort? Or are we down to the feature level of PHP (or below) when it comes to JSON requests? Should I not use such advanced technology and revert my client-side code to plain old HTTP form data instead?
I think I used this before and saw it working. But can't find out what the difference is here. Maybe it only works in controllers with the [ApiController] attribute? This case is a regular web page controller, not a separate API. But it needs to provide functions to JavaScript as well, so it does both.
I tried adding the [FromBody] attribute to all parameters
This sounds fishy to me. Are you trying to send your params as a json object and expecting them to get unwrapped into individual action params?
Consider the following data type and MVC controller endpoint:
public class Sample
{
public int Id { get; set; }
public string Name { get; set; }
}
[HttpPost]
public IActionResult Post([FromBody] Sample sample)
=> new JsonResult(sample);
This is all you need for a typical POST to an MVC controller. The key point is probably the type that I'm using to bind to the body. As you can see, I create a matching json object in Postman and it binds correctly and returns what I sent.
To get what you want, I think you'd have to rely on query params. (or some other technique I'm unaware of) Here's an example if you need it.
[HttpPost]
public IActionResult PostQuery([FromQuery] int id, [FromQuery] string name)
=> new JsonResult(new Sample {Id = id, Name = name});

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.

Sending array of strings to web api using postman

Here is a screenshot of my postman screen where I am attempting to make a get request to a web api call.
I also tried to make the call using [] with no numbers for each parameter and this did not work either. In both cases, the filenames array of string ended up being null. Disregard the error at the bottom of the screen as I replaced the actual api call with a sample. I saw the null value when I hit the breakpoint for the correct api call. Am I missing something here?
Given your action:
public async Task<IHttpActionResult> Test(string[] filenames) {
// ...
}
You need to add a [FromUri] attribute to your string[] filenames parameter. The reason is documented here:
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.
Arrays are considered a complex type. The [FromUri] attribute will override the default behaviour and instruct Web API to try and bind this parameter from the URI.

Create a RESTful service with 3 similar Get methods

I want to create a REStful web service.
This service returns "Meetings" for a certain
day, week or months.
I would create 3 Get methods which takes date values determined by the API user.
GetMeetingsByDay(currentDate)
GetMeetingsByWeek(startDate,endDate)
GetMeetingsByMonth(startDate,endDate)
Having 3 Get methods I could not access the API anymore just via http-method type 'GET' like:
// GET: api/meetings/date
because there will be multiple GET...
I could merge the ByWeek and ByMonth methods and do this instead:
GetMeetings(startDate,endDate);
How would you make this service RESTful?
I wouldn't think of it in terms of the method names - I'd think of it in terms of the URLs to start with:
/api/meetings?date=...
/api/meetings?startDate=...&endDate=...
Think of it as a collection of meetings, and the query parameters are just that - query parameters. I would expect any value after the meetings element in the URL to be a meeting ID - for example, your GetMeetings method might return a meeting with an ID of foobar which I'd then expect to be able to fetch later with
/api/meetings/foobar
That suggests to me that you shouldn't have date as part of the URL path at all.
In terms of implementation, I don't know enough about WebAPI routing to know whether you could implement that with two methods of:
[HttpGet]
[Route("api/meetings")]
public ... GetMeetings([FromUri] DateTime startDate, [FromUri] DateTime endDate)
[HttpGet]
[Route("api/meetings")]
public ... GetMeetings([FromUri] DateTime date)
... or whether you need to a single method with optional parameters:
[HttpGet]
[Route("api/meetings")]
public ... GetMeetings(
[FromUri] DateTime? date = null,
[FromUri] DateTime? startDate = null,
[FromUri] DateTime? endDate = null)
In the latter case you'd need to then validate that the set of arguments provided was valid.
(As noted in comments, you may not need the Route here at all.)
Web API 2 supports a new type of routing, called attribute routing.
This is the scenario where you need to use attribute routing.
eg:
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
One advantage of convention-based routing is that templates are
defined in a single place, and the routing rules are applied
consistently across all controllers. Unfortunately, convention-based
routing makes it hard to support certain URI patterns that are common
in RESTful APIs.
Read more here :
Attribute Routing in ASP.NET Web API 2
Create a REST API with Attribute Routing in ASP.NET Web API 2
From a previous good answer (source):
If you have a multiple Get actions that have a single primitive argument of the same type, ASP.NET Web API will look at the name of the argument to resolve which overloaded action to call.
For example, if you have two actions:
GetProductByName(string name)
GetProductByCategory(string category)
your http client can call as :
api/products?name=hammer
api/products?category=toys
and the routing engine will call the correct action.
The SO question, How does ASP.NET Web.api handle two methods with names starting with GET? can give you some more knowledge as it had been well answered.
EDIT :
I could merge the ByWeek and ByMonth methods and do this instead:
GetMeetings(startDate,endDate);
Definitely I would encourage this if you don't have any specific logic going into the GetMeetingsbyWeek and GetMeetingsbyMonth, like it merely fetches a list from database between the specified date range.
Look at Web API routing - http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
Otherwise, you could use just a querystring - like api/meetings/date?currentDate=x&startDate=x&endDate=x
For creating a WebApi Controller with multiple GET requests, You need to specify the route as follows:
routeTemplate:api/{Controller}/{action}/{param}
This should solve your problem.

WebAPI routing to a function with multiple optional parameters

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.

Categories