I want to implement multiple Get Methods, for Ex:
Get(int id,User userObj) and Get(int storeId,User userObj)
Is it possible to implement like this, I don't want to change action method name as in that case I need to type action name in URL.
I am thinking of hitting the action methods through this sample format '//localhost:2342/'
which does not contains action method name.
Basically you cannot do that, and the reason is that both methods have same name and exactly the same signature (same parameter number and types) and this will not compile with C#, because C# doesn't allow that.
Now, with Web API, if you have two methods with the same action like your example (both GET), and with the same signature (int, User), when you try to hit one of them from the client side (like from Javascript) the ASp.NET will try to match the passed parameters type to the methods (actions) and since both have the exact signature it will fail and raise exception about ambiguity.
So, you either add the ActionName attribute to your methods to differentiate between them, or you use the Route Attribute and give your methods a different routes.
Hope that helps.
You need to add action name to the route template to implement multiple GET methods in ASP.Net Web API controller.
WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional }
);
Controller:
public class TestController : ApiController
{
public DataSet GetStudentDetails(int iStudID)
{
}
[HttpGet]
public DataSet TeacherDetails(int iTeachID)
{
}
}
Note: The action/method name should startwith 'Get', orelse you need to specify [HttpGet] above the action/method
Related
We have a Desktop Client and a REST API. For several POST REST calls, we want to pass a correlation GUID from the Client to the API that is used to correlate log entries related to the same user interaction.
This parameter is optional and must be backwards compatible, not failing if it is omitted. What options are there to pass this in while keeping code clean?
I'm concerned that if I put it:
In the header, it means receiving a HttpRequestMessage and manually extracting it, the URL parameters and the body content in each API call.
In the URL, it may not hit the correct route if the parameter is missing (eg. backward compatibility.)
In the body, it pollutes the model as the parameter is related to the system rather than the data.
For example, an existing method is:
[HttpPost]
public HttpResponseMessage SaveData([FromBody]DataModel model)
{
...
}
I feel like I've missed something. Where is the best place for it?
It's strongly depends on your api action either is Get or Post, if you using Get action you can put your parameter in header and also you can use default value for your parameter in route configuration like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { **id = RouteParameter.Optional** }
);
If you are using Post action you can easily use default value in method prototype :
Example :
[HttpPost]
public object DoSomething(int logEntryIndex=0)
{
return "Something";
}
But I'm strongly recommend you always use an object as your parameter. Then it's so easy to add any property [as a new parameter] to class and you never would had these kind of problems
[HttpPost]
public object DoSomething(MyParam parameter)
{
return "Something";
}
public class MyParam
{
// prop1
// prop2
}
You can pass the correlation GUID from URL as optional parameter so that it will be backward compatible, clean and will not pollute your data models. For example:
[HttpPost]
[Route("api/Contact/{correlationId:Guid?}")]
public HttpResponseMessage SaveData([FromBody]DataModel model, [FromUri] Guid? correlationId = null)
{
//code to handle request
}
From MSDN:
You can make a URI parameter optional by adding a question mark to the
route parameter. If a route parameter is optional, you must define a
default value for the method parameter.
More information here
Hope it helps.
I have two APIs,
[HttpGet]
public bool WithoutParamBooleanResponse()
and
[HttpGet]
public string ComplexReferenceTypeParamStringResponse([FromUri]ComplexRefType VariableComplexRef)
However, this leads to having error
multiple actions were found that match the request web api get.
If I were to add another dummy parameter for the second method, the whole thing works. Could someone explain why a parameterless method and a method with a complex parameter are seen similar by the API ?
why a parameterless method and a method with a complex parameter are
seen similar by the API ?
When a parameter is annotated with FromUri attribute and is a complex type, the value is constructed from the query params, therefore the route for both methods would be the same (since the query params are not taken into account).
Try to create a new route like:
config.Routes.MapHttpRoute(
name: "ComplexRefType",
routeTemplate: "api/{controller}/{action}/{VariableComplexRef}",
defaults: new { VariableComplexRef = RouteParameter.Optional }
);
and try to add attribute on your action
[Route("ComplexReferenceTypeParamStringResponse/{VariableComplexRef?}"]
You need to add an action to your routing url.
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }
When calling a route and only passing in a controller, the routing assumes there is only one action for each method(GET,POST..) and looks for it. This is why you are having an error with more than one GET.
When you also pass an action, it is more specific to look for the correct action with this method
Being new and transitioning from ruby on rails. I would like to understand how to change the default routes to use custom routing.
For eg the current routes in my WebApiConfig.cs is as follows:
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new {id = RouteParameter.Optional}
So the URL GET api that I have configured in my controller is:
https://localhost:44300/api/Controller?type=FirstType
The corresponding method defined in my controller is:
public HttpResponseMessage GetControllerByType(string type)
What are the changes to be made to execute the same as above when calling the WEB api using the URL:
https://localhost:44300/api/Controller/FirstType
Assuming that you're using WebAPI 2 you can create custom routes using attributes on your actions within the controllers.
Add the following to the Register method of your WebApiConfig.cs:
config.MapHttpAttributeRoutes();
The above should be added BEFORE the default routing configuration that you already posted above.
Then within your controllers you can use annotations to describe routes:
[Route("controller/{type}")]
[HttpGet]
public HttpResponseMessage GetControllerByType(string type)
Values that are inside the brackets represent variables that are bound to the parameters of the method via their names.
You can also annotate classes themselves to have prefixes that apply to all methods within that controller:
[RoutePrefix("stuff")]
public class MyController : ApiController
{
[Route("myAction/{id}")] //route to this is via /stuff/myAction/{id}
[HttpGet]
public HttpResponseMessage MyMethod(string id)
{ ... }
}
The only 'gotcha' is that query strings should NOT be within the route template, those are represented by optional parameters (those with default values) in your method signature, which don't also have an optional annotation within the route template string. (ie: "myAction/{id:string?}" must have a default value in the signature, but all other parameters that aren't id with default values can be assigned via a query string)
Anything that you do not provide a route template for using an annotation will fallback to using the default routes specified in the WebApiConfig, which is by default "/api/controller_name/action_name/"
Here's a great tutorial on all the custom routing using attributes that you can setup: Attribute Routing in ASP.NET WebAPI 2
I'm trying to create a REST api based on the Asp.Net MVC4 web api framework. The GET requests are working just fine. All other verbs are getting ignored by the server.
It just says the following:
"No action was found on the controller 'Track' that matches the request."
Though the example error is from the Track controller all other controllers have the same problem.
This is the method on the controller Track I'm trying to call:
[HttpPost]
public Object Index(string token, string juke, string track)
{
}
I'v tried it using a JSON object like so:
{ "track": "0000", "juke": "0000" }
And I tried to use the "normal" way:
track=0000&juke=0000
The '0000' in the examples above are stand-ins for the real id's.
To be sure I'm also posting the Register() from WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.Routes.MapHttpRoute(
name: "DefaultIndexBackEnd",
routeTemplate: "back-end/{controller}/{token}",
defaults: new { action = "Index", token = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultBackEnd",
routeTemplate: "back-end/{controller}/{action}/{token}",
defaults: new { token = RouteParameter.Optional }
);
}
Try this in your TrackController. Because you are using multiple parameters, they must be declared as optional.
[HttpPost]
public Object Index(string token="", string juke="", string track="")
{
}
You can make parameter token mandatory because token is declared as optional in routing configuration DefaultIndexBackEnd. I think using [FromBody] attribute is a good idea when there is more than one parameter for POST actions.
First, it's important to understand how a typical REST Web API is supposed to work. Typically, it uses different HTTP verbs (GET, POST, PUT, DELETE) for specific actions.
GET: get an entity (or a collection) from the server
POST: create a new entity
PUT: update an existing entity
DELETE: delete an existing entity
So when I see [HttpPost] on your Index action, it seems to me that the REST API pattern is broken.
Instead, in your controller you should have a Entity Get(int id) action (to get data) and a void Post(Entity entity) action to create new records.
No need to decorate your actions with HttpGet or HttpPost, the MVC Api framework will route the request to your action based on their names.
You can take a look at an example here.
After several hours of trying and researching articles I finally found an article that precisely described my problem! If you're having the same problems, check out the article.
The problem was that I had to use the [FromBody] attribute on one parameter of the of the action. After moving juke and track into a model it finally worked as I had hoped.
Thanks everyone for the help, you all set me on the right track!
So, we're developing a webapi-heavy application that serves multiple types of clients (including a website that's a JavaScript client and multiple C# clients), and we'd like to use attribute routing to make friendlier urls. Something like this:
[RoutePrefix(Constants.RoutePrefixes.Api + "foo")]
[HttpPut]
FooController : ApiController
{
[Route("{fooId}/sub/{subFooId}")]
[HttpPut]
public HttpResponseMessage UpdateAFoo(int fooId, int subFooId)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
}
And a request to /api/foo/1/sub/2 would hit this controller, as expected.
However, we have a message handler built into one of our default routes for processing identity tokens, and requests coming from clients that require that special behavior still have to use that route, or at least something that runs the appropriate message handler, and the following request (which uses that other route):
/integration/foo?fooId=1&subFooId=2
..gives me an error that no suitable controller could be found to handle the request. If I remove the route attribute, this second request hits my action method as expected.
I've read in several places that you can use both default and attribute routes, and I have both working in the application, but I haven't been able to use both for a specific action.
My route config looks like this:
public static void RegisterHttpRoutes(HttpConfiguration configuration)
{
var routes = configuration.Routes;
configuration.MapHttpAttributeRoutes();
routes.MapHttpRoute(
name: Constants.RouteNames.Api,
routeTemplate: Constants.RoutePrefixes.Api + "{controller}/{id}/{p}",
defaults: new { id = RouteParameter.Optional, p = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: Constants.RouteNames.IntegrationApi,
routeTemplate: Constants.RoutePrefixes.IntegrationApi + "{controller}/{id}/{p}",
defaults: new { id = RouteParameter.Optional, p = RouteParameter.Optional },
constraints: null,
handler: new SuperDuperAuthenticationHandler())
);
}
Is that just how it works? Do I need a separate action without the route attribute? Is there a way to specify message handlers for attribute routes? Can I have both attribute and convention routing apply to the same action method? I'm not really clear on what my options are.
Controllers/Actions which are decorated with attribute routes cannot be reached via routes matched by conventional routing...so the behavior that you are seeing is expected...
Per-route message handlers are not supported for attribute routes.
Can you use AuthenticationFilterAttribute for your scenario?...if you need this filter for a set of controllers, then you could probably create a base controller decorated with this filter and let all these set of controller derive from it...