In a controller, I have a method like this:
public class FooController : ApiController
{
[HttpPost]
public HttpResponseMessage ApplySomething(int id)
{
...
}
}
When I do a POST request like .../Foo/ApplySomething/ and passing id=1 as POST values, I get the error:
{
Message: "No HTTP resource was found that matches the request URI '.../Foo/ApplySomething/'."
MessageDetail: "No action was found on the controller 'Foo' that matches the name 'ApplySomething'."
}
But when I change the URL to have the ID (like .../Foo/ApplySomething/1) it works, but getting the value from the URL, not from POST values.
What I'm doing wrong?
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.
Given those rules, if you want to bind the parameter from the POST body simply add a [FromBody] attribute in front of the type:
public HttpResponseMessage ApplySomething([FromBody] int id) { ... }
For more information please see the documentation.
Try this :
public class FooController : ApiController
{
[HttpGet]
public HttpResponseMessage ApplySomething(int? id=0)
{
...
return View();
}
[HttpPost]
public HttpResponseMessage ApplySomething(FormCollection Form,int? id=0)
{
...
return View();
}
}
Now Try this url .../Foo/ApplySomething?id=1
Hopefully it works...!
mention the httpget method with same action or
check you routeconfig file.
you can also refer following solutions:
solution 1: No HTTP resource was found that matches the request URI in Web API
solution 2: http://forums.asp.net/t/1916587.aspx?WebAPI+No+HTTP+resource+was+found+that+matches+the+request+URI
solution 3: http://our.umbraco.org/forum/developers/extending-umbraco/46086-UmbracoApiController-No-HTTP-resource-was-found-that-matches-the-request-URI
Related
I have the following api definition
[RoutePrefix("api/lead/1.0")]
public class LeadController:ApiController
{
[Route("/{id:integer}/request-td")]
[HttpPost]
public IHttpActionResult SubmitLead(int id,
FromBody]FormData FormData)
{
}
}
When my QA department is testing this code, they are calling
/api/lead/1.0/12345/request-td
with a valid body and everything passes
However if they change the id from an int to a string,
/api/lead/1.0/invalidid/request-td
They are getting back an iis 404 message.
As a temp solution I have changed the id from an int to a string, and removed the definition from the route.
Within the controller is performing a TyParse to make sure that a valid integer has been passed in the url
However this solution is not very elegant for me
Is there any way so that i can leave the id field defined as an int, with the correct type defined in the route, trap the invalid api request, and then send back my own custom error. ie different http status code and message body
If I have the signature as an int, but with no variable definiton in the route,
[Route("/{id}/request-td")]
[HttpPost]
public IHttpActionResult SubmitLead(int id,
FromBody]FormData FormData)
It is sending back too much information as to why the request is invalid
What I have seen so far, is that you need to get the definitions created in the correct order in global.asax not how to trap invalid api requests and return my own response
If you want receive "string" or "int" as request parameter then you need to write multiple time your "Route" attribute to add it in routing map.
Following is solution to receive values.
[Route("{id:int}/request-td")]
[Route("{id:alpha}/request-td")]
This will accept both "string" and "int" request and don't 404 page.
Hope this code will help you.
I would actually create a custom attribute.
public class CheckArgumentTypeAttribute : ActionFilterAttribute
{
private readonly string ActionArgumentName;
public CheckArgumentIsPositiveAttribute(string actionArgumentName)
{
if (string.IsNullOrWhiteSpace(actionArgumentName)) throw new ArgumentException(nameof(actionArgumentName));
ActionArgumentName = actionArgumentName;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var keyValuePair = actionContext.ActionArguments.FirstOrDefault(x => x.Key.Equals(ActionArgumentName, StringComparison.OrdinalIgnoreCase));
if (keyValuePair.Equals(default(KeyValuePair<string, object>)) || !int.TryParse(keyValuePair.Value, out in result))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(JsonConvert.SerializeObject(new YourClass())), Encoding.UTF8, MimeTypes.Application.Json)
};
}
}
}
Then I would decorate my action methods as
[CheckArgumentType("id")]. It is possible to create dynamic error message based on the ActionArgumentName or to further extend this attribute.
This question already has an answer here:
Is it possible to create a parameter binding on both FromURI and FromBody?
(1 answer)
Closed 4 years ago.
I have to develop a REST API that have to handle POST request (taking a payload from the body of my request) and at the same time taking an URI parameter.
Into my controller class I have something like this:
public class MailProtocolloController : ApiController
{
[SharePointContextWebAPIFilter]
[HttpPost]
[ActionName("protocollo/mail")]
public IHttpActionResult InviaAlProtocollo(XXX)
{
return Ok("TEST");
}
}
My problems are:
As you can see I am using the ActionName annotation to specify the URL path handled by this controller method. How can I specify the URI parameter into this ActionName annotation? (can I?) It have to handle something like this: protocollo/mail/{id}
What input parameter have to be specified for this controller method? One for the ID retrieved from the URI (a string) and an object for the request payload? Or this payload can be retrieved inside the method?
How can I implement this behavior?
Try this:
public class MailProtocolloController : ApiController
{
[SharePointContextWebAPIFilter]
[HttpPost]
[Route("protocollo/mail/{id}")]
public IHttpActionResult InviaAlProtocollo([FromUri] string id, [FromBody] string context)
{
return Ok("TEST");
}
}
You can use the RouteAttribute to define route mappings where you can use parameter placeholders to get those from the url path.
And you can also get the body of the request if you decorate your parameter which represents the body with the FromBody attribute:
[RoutePrefix("protocollo")]
public class MailProtocolloController : ApiController
{
[SharePointContextWebAPIFilter]
[HttpPost]
[Route("mail/{id}")]
public IHttpActionResult InviaAlProtocollo(string id, [FromBody]string body)
{
return Ok("TEST");
}
}
I like the way in WebAPI how you just put a defined model as the parameter in a method of a controller. I'm switching to OData and every example I've found of working with a data structure is by defining it in the modelBuilder
var action = builder.Action("MyAction").ReturnsCollection<int>();
action.Parameter<int>("ProgramId");
action.Parameter<int>("BrandId");
action.Parameter<int>("StoreId");
action.Parameter<OperationStatus>("Status");
and then in the controller you must manually get those values from the ODataParameter dictionary. I find this tedious.
Also in the WebAPI way you can catch errors at compile time or by VisualStudio intelisense, and do validations with annotations.
Are there other ways to pass the parameters to an action? Something more like the WebAPI "perks"
You can have a custom DTO as a parameter for an OdataAction.
For example the code for registering would be like:
var action = builder.Action("MyAction");
action.Parameter<OperationStatus>("Status");
action.ReturnsCollection<int>("Results");
And the Json payload for the request would be like:
{
"Status": {
"Property1":"value",
"Property2":"value"
}
}
Action declaration in controller:
[HttpPost]
[ODataRoute("MyAction")]
public async Task<IHttpActionResult> MyAction(ODataActionParameters parameters)
{
OperationStatus status;
if (!parameters.TryGetValue("Status",out status))
{
return BadRequest("Missing parameter Status");
}
}
I am making an HTTP POST method to get data. I have an idea to create a method to get a specific arguments but when I don't have idea to get the arguments taken. In HTTP GET, arguments are in the URL and is more easy to get the arguments. How do I create a method to take all the data in HTTP Post? In PHP for example when you show the var $_POST you show all data in the body post. How can I do this in C#?
My method is this:
[HttpPost]
[AllowAnonymous]
public IHttpActionResult Test()
{
// Get URL Args for example is
var args = Request.RequestUri.Query;
// But if the arguments are in the body i don't have idea.
}
Web API has a feature which automatically binds argument posted to an action inside a controller. This is called Parameter Binding. It allows you to simply request the object inside the URL or the body of the POST request, and it does the deserialization magic for you using a thing called Formatters. There is a formatter for XML, JSON, and other known HTTP request types.
For example, lets say I have the following JSON:
{
"SenderName": "David"
"SenderAge": 35
}
I can create an object which matches my request, we'll call it SenderDetails:
public class SenderDetails
{
public string SenderName { get; set; }
public int SenderAge { get; set; }
}
Now, by receiving this object as a parameter in my POST action, I tell WebAPI to attempt to bind that object for me. If all goes well, I'll have the information available to me without needing to do any parsing:
[Route("api/SenderDetails")]
[HttpPost]
public IHttpActionResult Test(SenderDetails senderDetails)
{
// Here, we will have those details available,
// given that the deserialization succeeded.
Debug.Writeline(senderDetails.SenderName);
}
If I get you Correctly, in C# you use the [HttpPost] attribute to expose a post method.
[HttpPost]
public IHttpActionResult Test()
{
// Get URL Args for example is
var args = Request.RequestUri.Query;
// But if the arguments are in the body i don't have idea.
}
I've just switched from AttributeRouting to WebApi 2.0 AttributeRouting, and have got a controller and action defined like so:
public class InvitesController : ApiController
{
[Route("~/api/invites/{email}")]
[HttpGet]
[ResponseType(typeof(string))]
public IHttpActionResult InviteByEmail(string email)
{
return this.Ok(string.Empty);
}
}
Example query:
GET: http://localhost/api/invites/test#foo.com
The response I receive is a 200, with empty content (due to string.Empty).
This all works fine -- but I want to change the email property to be a query parameter instead . So I update the controller to:
public class InvitesController : ApiController
{
[Route("~/api/invites")]
[HttpGet]
[ResponseType(typeof(string))]
public IHttpActionResult InviteByEmail(string email)
{
return this.Ok(string.Empty);
}
}
But now when querying the endpoint with:
GET: http://localhost/api/invites?email=test#foo.com
The response I receive is a 404:
{
"message": "No HTTP resource was found that matches the request URI 'http://localhost/api/invites?email=test#foo.com'.",
"messageDetail": "No route providing a controller name was found to match request URI 'http://localhost/api/invites?email=test#foo.com'"
}
Does anyone know why it doesn't match the route when the parameter is swapped to a query parameter, rather than inline of the url ?
As requested, WebApiConfig is defined like so:
public static void Register(HttpConfiguration config)
{
var jsonFormatter = config.Formatters.JsonFormatter;
jsonFormatter.Indent = true;
jsonFormatter.SerializerSettings.ContractResolver = new RemoveExternalContractResolver();
config.MapHttpAttributeRoutes();
}
Thanks !
I think you need to include the query parameter (along with its type) in the Route as follows:
[Route("api/invites/{email:string}")]
using it will be
POST: http://localhost/api/invites/test#foo.com
Alternatively if you want to name the query parameter:
[Route("api/invites")]
using it will be (as long as you have an email parameter in your method)
POST: http://localhost/api/invites?email=test#foo.com
As you comment in edhedges answer: the route template cannot start with a '/' or a '~', so you can remove that from the route as above
Issue is a clash of route definitions, which went unnoticed due to being cross-controller (and also some of the routes being 'absolute' (~/)). Below is an example that reproduces the result.
public class ValuesController : ApiController
{
[Route("~/api/values")]
[HttpGet]
public IHttpActionResult First(string email)
{
return this.Ok("first");
}
}
[RoutePrefix("api/values")]
public class ValuesTwoController : ApiController
{
[Route("")]
[HttpGet]
public IHttpActionResult Second(string email)
{
return this.Ok("second");
}
}
Issuing a request:
GET: http://localhost/api/values?email=foo
Will return a 404 with a response of:
{
"message": "No HTTP resource was found that matches the request URI 'http://localhost/api/values?email=foo'.",
"messageDetail": "No route providing a controller name was found to match request URI 'http://localhost/api/values?email=foo'"
}
What is misleading is the response message.
I think you need to change your Route declaration to this: [Route("~/api/invites?{email}")]
Here is a relevant link: http://attributerouting.net/#route-constraints