Post data through Url to webapi - asp.net-web-api

Hej,
I am unable to post data to the action method through querystring to the action method which is located in the controller class below is my code.
I type a url "http://localhost:53459/api/esb/post/test" to post value and nothing happens
Any help would be appreciated.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{value}",
defaults: new { value = RouteParameter.Optional }
);
}
}
[RoutePrefix("api/esb")]
public class EsbController : ApiController
{
[Route("get")]
[HttpGet]
public string Get()
{
return "Hello there!";
}
[Route("post")]
[HttpPost]
[AcceptVerbs("POST")]
public string Post([FromUri]string value)
{
return string.Format("{0} is posted successfully ", value);
}
[Route("put")]
[HttpPut]
public string Put([FromUri] string value)
{
return string.Format("{0} is updated successfully ", value);
}
[Route("delete")]
[HttpDelete]
public string Delete(string value)
{
return string.Format("{0} is deleted successfully ", value);
}
}

If you are typing the url into a browser you are constructing a GET request so it will never reach your Post action. You can confirm this by adding "GET" to the allowed verbs on the action (note: remove the [HttpPost] attribute).
[Route("post")]
[AcceptVerbs("POST", "GET")]
public string Post([FromUri]string value)
{
return string.Format("{0} is posted successfully ", value);
}

remove parameter binding [FromUri] and update the Route like
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{value}",
defaults: new { value = RouteParameter.Optional }
);
}
}
Controller
[RoutePrefix("api/esb")]
public class EsbController : ApiController
{
[Route("get")]
[HttpGet]
public string Get()
{
return "Hello there!";
}
[Route("post/{value}")]
[HttpPost]
[AcceptVerbs("POST")]
public string Post(string value)
{
return string.Format("{0} is posted successfully ", value);
}
}
This is working for me, try to use Postman in chrome or fiddler.
POST http://localhost:XXXXX/api/esb/post/test HTTP/1.1

For dot.Net Core (I'm using v2.0), use FromRoute
[AllowAnonymous]
[HttpPost]
[Route("validateEmail/{validationCode}")]
public async Task<IActionResult> ValidateEmail(
[FromRoute] Guid validationCode)
{
await authService.ValidateEmailAsync(validationCode);
return Ok();
}
Then post like this:

Related

Web API multiple get

I have couple of get methods in my web API class. The route config is as below;
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/test/{action}/{id}",
defaults: new { controller = "item", action="testBring", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The controller class is as below;
public class ItemController : ApiController
{
// GET: api/Item
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[ActionName("testBring")]
[HttpGet]
public string testBring()
{
return "value";
}
// GET: api/Item/5
public string Get(int id)
{
return "value";
}
// POST: api/Item
[HttpPost]
public void CreateItem([FromBody]string value)
{
}
// PUT: api/Item/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Item/5
public void Delete(int id)
{
}
}
When I try to call /api/test, testBring method gets called. But when I call /api/Item I get an error that multiple actions were found that match the request ... Why is it so?
/api/test maps fine to the first route template api/test/{action}/{id} because of the default parameters included, which allow it to call a specific action.
However, /api/Item maps to the second route template api/{controller}/{id} and the route table is unable to determine which action you wanted since there are multiple HttpGet actions (ie Get() and testBring() ) that match the requested path.

How do I route to a method in ASP.NET Web API?

According to the documentation, if I have this in my WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I should be able to route to a method in my API controller using a URL like this:
http://localhost:55601/api/Customers/Search
Here is my method:
[ResponseType(typeof(int))]
[HttpPost]
public IHttpActionResult Search([FromBody]CustomerDTO SearchTerm)
{
string Name = SearchTerm.Name;
string Email = SearchTerm.Email;
string PhoneNumber = SearchTerm.Phone;
var customer = db.Customers.Single(c => c.Name == Name && c.EmailAddress == Email && c.PhoneNumber == PhoneNumber);
return Ok(customer.id);
}
I'm sending the search data as a JSON object (using HTTP POST method) in the request body.
However, I get an error saying:
Multiple actions were found that match the request
I only have one method in this controller called Search.
I would have thought this should be pretty straightforward, and work the same way it does with MVC controllers. But I think I'm missing something obvious. Can anyone tell me what it is?
EDIT: As per #KevinLaw's request, adding code for controller showing upblic methods. Also, for further information the following request (HTTP GET) works as expected:
http://localhost:55601/api/Customers?email=[recipient#domain]
public class CustomersController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: api/Customers
public IQueryable<Customer> GetCustomers()
{
//...
}
// GET: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(int id)
{
//...
}
// GET: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomerByEmail(string email)
{
//...
}
// PUT: api/Customers/5
[ResponseType(typeof(void))]
public IHttpActionResult PutCustomer(int id, Customer customer)
{
//...
}
// POST: api/Customers
[ResponseType(typeof(Customer))]
public IHttpActionResult PostCustomer(Customer customer)
{
//...
}
[ResponseType(typeof(int))]
[HttpPost]
public IHttpActionResult SearchCustomer([FromBody]CustomerDTO SearchTerm)
{
//...
}
// DELETE: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult DeleteCustomer(int id)
{
//...
}
}
The problem here is that the WebApiController uses the REST API specs.
Which state that in a Web Api Controller there can be Zero - One Http Verb.
What i mean by that is that you can have one GET,PUT,POST,DELETE,PATCH
The reason you don't have any problem with the GET is because you have them correctly overloaded. () (int) (string).
But your Posts is (Customer) (CustomerDTO) They are both complex objects and the Binder cannot identify which is which when binding to the complex object.
For this to work you need to use Route Attributes or explicit route.
Attribute Routing
Explicit Routing pt1
Explicit Routing pt2
I think the links are enough to get you started.
If you still want to see some code on your specific case leave a comment below and i will give you some examples.
Thanks
EDIT: Added Examples
Attribute Routing
On WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
}
On Controller
[RoutePrefix("api/test")]
public class TestingController : ApiController
{
[HttpGet]
[Route("")]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpPost]
[Route("search")]
public IHttpActionResult Post([FromBody]SearchCriteria criteria)
{
return Ok(criteria);
}
}
public class SearchCriteria
{
public string Name { get; set; }
}
Explicit Routing
On WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes config.Routes.MapHttpRoute(
name: "SearchTest",
routeTemplate: "api/test/search",
defaults: new { controller = "Testing", action = "Search" }
);
config.Routes.MapHttpRoute(
name: "TestingController",
routeTemplate: "api/test/{id}",
defaults: new { controller = "Testing", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
On Controller
public class TestingController : ApiController
{
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpPost]
public IHttpActionResult Search([FromBody]SearchCriteria criteria)
{
return Ok(criteria);
}
}
public class SearchCriteria
{
public string Name { get; set; }
}

Web api 2 attribute routing not working even with it configured in WebApiConfig

I don't understand why my attribute routing isn't working. If I go to 'http://localhost:56125/api/Contact/1682' then I get the test controller with "value1" and "value2" shown, but if I go to http://localhost:56125/api/contacts/1682 then I get No HTTP resource was found that matches the request URI 'http://localhost:56125/api/contacts/1682' and I don't understand why?
In ContactController.cs:
public class ContactController : ApiController
{
private readonly NGSystemRepository _repo = new NGSystemRepository();
// GET: api/Contact
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[Route("api/contacts/{contactId}")]
[HttpGet]
public ContactInformation GetContactInformation(int contactNumber)
{
return _repo.GetContact(contactNumber);
}
}
In WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Argh! Stupid mistake. I didn't match the attribute property name (contactId) with the method property name (contactNumber).
My controller action should simply be:
[Route("api/contacts/{contactId}")]
[HttpGet]
public ContactInformation GetContactInformation(int contactId)
{
return _repo.GetContact(contactNumber);
}

No HTTP resource was found that matches the request URI error in ASP.NET Web API

This is a sketch of my TransferController class.
All this is Web API code.
public class TransferController : ApiController
{
[HttpGet, ActionName("Queue")]
public IEnumerable<object> GetQueue(Guid sessionId) {...}
[HttpDelete, ActionName("Delete")]
public void Delete(Guid sessionId, Guid fileId) {...}
[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid fileId) {...}
[HttpGet, ActionName("UploadedBytes")]
public long GetUploadedByteCount(Guid sessionId, Guid fileId) {...}
[HttpGet, ActionName("DownloadUrl")]
public string GetDownloadUrl(string fileId) {...}
[HttpPost, ActionName("FileChunk")]
public void PostFileChunk([FromUri]Guid sessionId, [FromUri]Guid fileId) {...}
[HttpPost, ActionName("UploadDefinition")]
public Guid PostUploadItem([FromBody]UploadDefinition uploadDef) {...}
}
This is the routing.
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}"
);
config.Routes.MapHttpRoute(
name: "DefaultApiDefaultMethod",
routeTemplate: "api/{controller}"
);
}
This is the invocation.
$.ajax({
url: "api/Transfer/Queue",
data: { sessiondId: login.SessionId() }
})
.done(function (result) {
history.push(new UploadItem());
for (var i = 0; i < result.length; i++) {
var ui = new UploadItem(result[i]);
history.push(ui);
}
})
.fail(function (result) {
app.showMessage(JSON.parse(result.responseText).Message);
});
And this is the result.
No HTTP resource was found that matches the request URI 'http://localhost:54770/api/Transfer/Queue?sessiondId=0e2c47b9-e674-446d-a06c-ce16932f9580'.
This is a sketch of my UserController class.
public class UserController : ApiController
[HttpGet, ActionName("Authenticate")]
public object Authenticate(string email, string password) {...}
[HttpPost]
public void Register([FromBody]UserDefinition userDef) {...}
[HttpGet, ActionName("Pulse")]
public bool Pulse(Guid sessionId) {...}
}
For reasons unfathomable to me, I have no trouble calling anything in the UserController. Parameters are marshalled in exactly the same way, and the same routes are in use.
Darrel Miller below uses unit testing to validate routes. Frankly I'm kicking myself for not thinking of this, and now I've done the same.
But tests as he shows them really test only parsing of the URL. For example, this test passes
public void TestMvc4RouteWibble()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var route =
config.Routes.GetRouteData(new HttpRequestMessage()
{
RequestUri = new Uri("http://localhost:54770/api/Transfer/Wibble?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580&fileId=0e2c47b9-e674-446d-a06c-ce16932f9581") //?
});
Assert.IsNotNull(route);
Assert.AreEqual("Transfer", route.Values["controller"]);
Assert.AreEqual("Wibble", route.Values["action"]);
}
despite the conspicuous absence of a method Wibble on the Transfer controller.
Also the route object is not actually a HttpRoute object, it's a HttpRouteData object. But that's trivially corrected. The HttpRoute object is available as a property of the HttpRouteData object.
public void TestMvc4RouteWibble()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var routeData =
config.Routes.GetRouteData(new HttpRequestMessage()
{
RequestUri = new Uri("http://localhost:54770/api/Transfer/Wibble?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580&fileId=0e2c47b9-e674-446d-a06c-ce16932f9581") //?
});
Assert.IsNotNull(routeData);
Assert.AreEqual("Transfer", routeData.Values["controller"]);
Assert.AreEqual("Wibble", routeData.Values["action"]);
}
And it in turn has a Handler property. However this is less informative than it might be, since a null handler simply means (from MSDN)
If null, the default handler dispatches messages to implementations of IHttpController.
Now, my controller is derived from ApiController which certainly implements the ExecuteAsync method that is the only thing specified by the IHttpController interface. Which I imagine means I could test execution of that method if I knew more about it.
Here is a test that demonstrates the routing works ok,
[Fact]
public void TestRoute()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}"
);
var route =
config.Routes.GetRouteData(new HttpRequestMessage()
{
RequestUri = new Uri("http://localhost:54770/api/Transfer/Queue?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580") //?
});
Assert.NotNull(route);
Assert.Equal("Transfer",route.Values["controller"]);
Assert.Equal("Queue",route.Values["action"]);
}
and here is a test showing the dispatching/action selection is also working,
[Fact]
public void TestDispatch()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}"
);
var server = new HttpServer(config);
var client = new HttpClient(server);
var response =
client.GetAsync(new Uri("http://localhost:54770/api/Transfer/Queue?sessionId=0e2c47b9-e674-446d-a06c-ce16932f9580")) //
.Result;
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
public class TransferController : ApiController
{
[HttpGet]
[ActionName("Queue")]
public IEnumerable<object> Queue(Guid sessionId)
{
return null;
}
}
OK then... thanks for putting the unit test idea in my head, it sped things up immensely.
Here's the lowdown:
You can have identical parameter signatures on different verbs (get post put delete).
You cannot have identical parameter signatures on different action names on the same verb.
You only need vary one parameter name.
So this is ok because they're all on different verbs
[HttpDelete, ActionName("Delete")]
public void Delete(Guid sessionId, Guid fileId) {...}
[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid fileId) {...}
[HttpPost, ActionName("FileChunk")]
public void PostFileChunk(Guid sessionId, Guid fileId) {...}
but this is not cool because they're both gets
[HttpGet, ActionName("UploadedBytes")]
public long GetUploadedByteCount(Guid sessionId, Guid fileId) {...}
[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid fileId) {...}
and you can fix it like this
[HttpGet, ActionName("UploadedBytes")]
public long GetUploadedByteCount(Guid sessionId, Guid uploadBytesFileId) {...}
[HttpGet, ActionName("Cancel")]
public bool Cancel(Guid sessionId, Guid cancelFileId) {...}
Maybe I'm a hard-ass but as far as I'm concerned it isn't routing until the method is called.

WebAPI Routing Assistance

Learning a lot about MapRouting and have spent WAY too much time trying to get what I need done, so I'm hoping someone can help out. :)
I'm looking to do the following:
/api/Entities/1 <- Views the details entity with id of 1 (this is a string, not int)
/api/Entities/1/Action <- Calls a particular action on the entity with the id of 1.
/api/Entities <- Views the entities set.
/api/Entities/Action <- Calls the particular action on the entities set.
The problem I'm encountering is the last one. That is currently being intercepted by the first case, as the id is a string.
Any assistance would be greatly appreciated!
A very good reference is here
Without using Attribute based routing the answer is somewhat long winded as the RPC style (Action) and Rest methods do not co-exist nicely if you are matching to the same verb. As you have noticed the GET() and DOSOMETHING() are seen as duplicate method signatures on the same controller. To get around this you could try use two controllers:
So I would recommend using attribute based routing; however, it is possible with normal methods:
Using standard routing...
Set your routes like so
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionDefaultApi",
routeTemplate: "api/entities/{action}/{actionId}",
defaults: new
{
//Add more as needed
action = "(|dosomething|dosomethingelse)",
actionId = RouteParameter.Optional,
controller = "EntitiesRpc"
});
Controller 1:
public class EntitiesController : ApiController
{
public string Get()
{
return "http://server/api/Entities";
}
public string Get(string id)
{
return string.Format("http://server/api/Entities/{0}", id);
}
}
Controller 2:
public class EntitiesRpcController : ApiController
{
[HttpGet]
public string DoSomething()
{
return "http://server/api/Entities/doSomething";
}
[HttpGet]
public string DoSomething(string actionId)
{
return string.Format("http://server/api/Entities/doSomething/{0}", actionId);
}
[HttpGet]
public string DoSomethingElse()
{
return "http://server/api/Entities/doSomethingelse";
}
[HttpGet]
public string DoSomethingElse(string actionId)
{
return string.Format("http://server/api/Entities/doSomethingelse/{0}", actionId);
}
}
Now assumming you used attribute based routing you could go back to one controller and use something like this:
public class EntitiesController : ApiController
{
[Get("Entities")]
public string Get()
{
return "http://server/api/Entities";
}
[Get("Entities/{id}")]
public string Get(string id)
{
return string.Format("http://server/api/Entities/{0}", id);
}
[Get("Entities/doSomething")]
public string DoSomething()
{
return "http://server/api/Entities/doSomething";
}
[Get("Entities/doSomething/{actionId}")]
public string DoSomething(string actionId)
{
return string.Format("http://server/api/Entities/doSomething/{0}", actionId);
}
[Get("Entities/doSomethingelse")]
public string DoSomethingElse()
{
return "http://server/api/Entities/doSomethingelse";
}
[Get("Entities/doSomethingelse/{actionId}")]
public string DoSomethingElse(string actionId)
{
return string.Format("http://server/api/Entities/doSomethingelse/{0}", actionId);
}
}

Resources