How to dynamically specify Query String in Web API URL - c#

I am new to c# and web api. I have a WebAPI controller with a Get method as follows:
public class peopleController : ApiController
{
[HttpGet]
public IHttpActionResult getAllPeople(string Name, string Age)
{
//Return something
}
}
My WebApiConfig is like this:
config.Routes.MapHttpRoute(
name: "getAllPeopleApi",
routeTemplate: "people",
defaults: new { controller = "people", action = "getAllPeople" }
);
If I invoke my url like this : http://localhost:xxx/people?Name=&Age=. It working fine.
But when I invoke like all these:
http://localhost:xxx/people,http://localhost:xxx/people?Name=,http://localhost:xxx/people?Age=
I got this error message:
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:xxxx/......'.","MessageDetail":"No action was found on the controller 'people' that matches the request."}
I try to set my routeTemplate: "people/{Name}/{Age}". Now when I run this web api Error 404.0 Not Found

routeTemplate: "people/{Name}/{Age}"
This part is not a QueryString, this is a dynamic path.
That means that your path becomes Domain/people/SomeName/SomeAge?QueryString=Whatever

Managed to solve this by explicitly set parameter Name and Age to Null.
public class peopleController : ApiController
{
[HttpGet]
public IHttpActionResult getAllPeople(string Name = null, string Age=null)
{
//Return something
}
}
This will made the parameters as an optional parameters. Now you can invoke the controller with or without Query string parameter.

Related

C# REST API Controller: same route with 2 different actions

When using the following routes:
config.Routes.MapHttpRoute(
name: "new_device",
routeTemplate: "api/v1/devices",
defaults: new { controller = "Devices", action = "new_device" }
);
config.Routes.MapHttpRoute(
name: "devices_list",
routeTemplate: "api/v1/devices",
defaults: new { controller = "Devices", action = "devices_list", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
The controller looks as follows:
public class DevicesController : ApiController
{
[HttpPost]
[ResponseType(typeof(IHttpActionResult))]
[Route("api/v1/devices")]
[ActionName("new_device")]
[ValidateModel]
public IHttpActionResult NewDevice([System.Web.Http.FromBody] Device device )
{
...
}
[HttpGet]
[ResponseType(typeof(IHttpActionResult))]
[Route("api/v1/devices")]
[ActionName("devices_list")]
[ValidateModel]
public List<Device> GetAllDevices()
{
...
}
My expectation would be that the router would find the correct route based on the HttpMethod used since even it's using the same URI it is using a different HttpMethod.
But instead it fails with the following:
"Message": "The requested resource does not support http method 'GET'."
My guess is because it fins a match with the URI and then checks if the method if the same.
Is there a way to achieve using the same URI with different Http Method which is by the way REST guidelines? Am I missing something?
Ok , I check your whole code. I think you are trying to achieve the calls in complicated way.
Following code is for the configuration :
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and follwoing is your controller code :
public class DevicesController : ApiController
{
[HttpPost]
[ResponseType(typeof(IHttpActionResult))]
[ActionName("newDevice")]
public IHttpActionResult NewDevice([System.Web.Http.FromBody] Device device)
{
return null;
}
[HttpGet]
[ResponseType(typeof(IHttpActionResult))]
[ActionName("devices_list")]
public List<Device> GetAllDevices()
{
return null;
}
}
I removed ValidateModel. I think it's your custom attribute or somehow related with built in nuget package.
Anyways, execute the calls with Postman or any HTTP client tool. It should work , as it was working at my end with above mentioned code.
Example Calls:
https://localhost:44370/api/v1/devices/devices_list = > Get.
https://localhost:44370/api/v1/devices/newDevice => Post. Provide body as post call for the object.

No HTTP resource was found that matches the request URI - and - No action was found on the controller that matches the request

I am trying to build out a new endpoint in API app that already has a lot of other endpoints working just fine. Not sure what I'm doing wrong. I am getting two errors:
Message: No HTTP resource was found that matches the request URI 'http://localhost:62342/api/VoiceMailStatus
and
MessageDetail: No action was found on the controller 'VoiceMailStatus' that matches the request.
Here's the controller:
public class VoiceMailStatusController : ControllerBase
{
[HttpPost]
[Route("api/VoiceMailStatus")]
public string VoiceMailStatus(string var)
{
...
}
}
And here's the route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I'm using PostMan:
There are a LOT of threads here about both of these error messages. I've read many of them, but have yet to find a solution. One of them said to change this:
public string VoiceMailStatus(string var)
to this:
public string VoiceMailStatus(string var = "")
And while that did get the error to go away and I was able to get inside of the method while in debug, var was always just an empty string.
EDIT: GOT IT WORKING
In addition to adding [FromBody] as per Andrii Litvinov's answer, I also had to do one more thing. What had been this:
public string VoiceMailStatus(string var)
{
...
}
Is now this:
public string VoiceMailStatus([FromBody] VMStatus request)
{
...
}
And then VMStatus is just a small little class with a single string property:
public class VMStatus
{
public string var { get; set; }
}
Most likely you need to apply FromBody attribute to your parameter:
public string VoiceMailStatus([FromBody] string var)
If that does not help try to rename parameter to something else, e.g.: var1, because var is reserved work in C# and could cause some binding issues, but I doubt that's the case.
You probably want to add this to your controller since you have api in the route config:
[RoutePrefix("API/VoiceMailStatus")]
and then add this to your action:
[Route("VoiceMailStatus", Name = "VoiceMailStatus")]
This should tie the action to the url localhost/api/voicemailstatus/voicemailstatus

No type was found that matches the controller named in asp.net website project

I'm facing the below error while ruing my asp.net website project.
Error:
No type was found that matches the controller named 'XXXX'.
Route Config:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
// AuthConfig.RegisterOpenAuth();
RouteConfig.RegisterRoutes(RouteTable.Routes);
System.Web.Http.GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
}
WebApi Controller:
public class SampleWebController : ApiController
{
public object SampleAction(Dictionary<string, string> jsonResult)
{
}
}
URL: ServiceUrl="../api/SampleWeb"
Please any one provide an idea to over come this error.
Also let me know if i'm doing any think wrong here.
Thanks in advance.
If you have multiple POST actions in the same controller you should make the Route Config like this:
System.Web.Http.GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
Then in your controller you can have multiple GET and POST methods
public class TestController : ApiController
{
[ActionName("PostMe")]
public object PostMe()
{
}
[ActionName("PostMeTwo")]
public object PostMeTwo()
{
}
[HttpGet]
public object TestGet()
{
}
}
Then you can generate a POST request to the action either using Ajax or PostMan like this:
localhost:XXXX/Test/PostMe
Where Test is name of controller and PostMe Name of action - Both Required
localhost:XXXX/Test/PostMeTwo[POST]
localhost:XXXX/Test/TestGet [GET]
You have 2 options.
First option, add an action name to your controller
public class SampleWebController : ApiController
{
[ActionName("SampleAction")]
public object SampleAction(Dictionary<string, string> jsonResult)
{
}
}
And you would call it like "../api/SampleWeb/SampleAction"
This way you don't need to change your route config
Second option, change your route config to
routeTemplate: "api/{controller}/{id}",
and your method to
public class SampleWebController : ApiController
{
[HttpGet]
public object GetSampleAction(Dictionary<string, string> jsonResult)
{
}
}
You can then call '../api/SampleWeb' if you are making a get request.
NOTE: If you are going to have multiple gets, posts, puts, etc in the same controller, go with the first option. If you only plan on a single get, post, put, etc for each controller then option 2 is much cleaner.
EDIT: To test option 1, change your method to
public class SampleWebController : ApiController
{
[ActionName("SampleAction")]
public object SampleAction(int id)
{
return id;
}
}
And call you api like '.../api/SampleWeb/SampleAction/10'. If this works it means the data you are passing to SampleAction can't be converted to Dictionary and thats your issue, not webapi or your routing. Make sure your route config is still
routeTemplate: "api/{controller}/{action}/{id}",

Web API routing in C#

I am trying to build a web API which would accept 2 parameters. However, upon calling the API it always hits the method without any parameters.
I followed the instructions in here, and can't figure why it won't work.
The request I send using the 'PostMaster' Chrome extension : http://localhost:51403/api/test/title/bf
For the above request, I expect the first method to be hit, but the second method is being reached.
The method within the controller are :
// Get : api/test/type/slug
public void Get(string type,string slug){
//Doesn't reach here
}
// Get : api/test
public void Get() {
// Reaches here even when the api called is GET api/test/type/slug
}
The webApiConfig hasn't been changed much, except that it accepts 2 parameters :
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id1}/{id2}",
defaults: new { id1 = RouteParameter.Optional, id2 = RouteParameter.Optional }
);
}
My understanding from the documentation is that the webapiconfig doesn't have to be changed.
Here's the error that I get
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:51403/api/test/title/bf'.",
"MessageDetail":"No action was found on the controller 'test' that matches the request."}
In order for the routing engine to route the request to the right Action, it looks for the method whose parameters match the names in the route first.
In other words, this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id1}/{id2}",
defaults: new { id1 = RouteParameter.Optional, id2 = RouteParameter.Optional }
);
matches:
public void Get(string id1, string id2) {}
but not:
public void Get(string type, string slug) {}
If you wanted to, this would have also worked:
http://localhost?type=weeeee&slug=herp-derp
which would have matched
public void Get(string type, string slug)
You must use parameters name named id1 and id2 that in your route config.
Like this:
// Get : api/test/type/slug
public void Get(string id1,string id2){
//Doesn't reach here
}

WebApi routing issue

I am using the default routing setup in WebApiConfig (MVC 4) but for some reason I am getting unexpected results.
If I call /api/devices/get/ it hits the Get function but the Id is "get" rather than 1. If I call /api/devices/get/1 I get a 404. I also want to be able to support multiple parameters i.e.
public Device[] Get(int? page, int? pageSize) // for multiple devices
The route
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
And my API:
public class DevicesController : ApiController
{
EClient client = new EClient();
// GET api/devices/5
public Device Get(string id)
{
return client.GetDeviceBySerial(id);
}
}
id in the controller parameter should be integer:
public Device Get(int id)
{
return client.GetDeviceBySerial(id);
}
if you need to pass in string, or other prams, just use quesry string:
public Device Get(int id, string pageSize)
{
return client.GetDeviceBySerial(id);
}
the above can be called as:
/api/devices/1
or
/api/devices/?id=1&pageSize=10
Note: you do not need to specify method name. Web API will judge that on the basis of HTTP Verb used. If its a GET request, it will use the Get method, if its a POST request, then it will use Post method ... and so on.
You can change the above behavior, but I guess you mentioned that you want to keep usign the default Routing ... so I am not covering that.

Categories