I'm having trouble getting the Web API 2 attribute routing to work.
I've been trying everything I could find this whole evening but I can't find the problem.
What I want to achieve is the following:
Make a POST request to http://localhost:xxxx/api/chat/joingroup/1234 to get to the following API call:
[Route("joingroup/{id}")]
[HttpPost]
public async Task<IHttpActionResult> JoinGroup(string id, string connectionID)
{
await hubContext.Groups.Add(connectionID, id);
return Ok(hubContext.Groups.ToString());
}
This keeps getting me a http 400 message.
{"message":"No HTTP resource was found that matches the request URI 'http://localhost:41021/api/chat/joingroup/123'.",
"messageDetail":"No action was found on the controller 'Chat' that matches the request."}
But sending a post to: http://localhost:41021/api/chat/sendmessage/pm/123123 and also to http://localhost:41021/api/chat/joingroup gives me a 200
The chatcontroller:
[RoutePrefix("api/chat")]
public class ChatController : ApiController
{
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
[...]
[Route("joingroup/{id}")]
[HttpPost]
public async Task<IHttpActionResult> JoinGroup(string id, string connectionID)
{
await hubContext.Groups.Add(connectionID, id);
return Ok(hubContext.Groups.ToString());
}
HTTP POSTS to http://localhost:xxxx/api/chat/sendmessage are working fine.
I cannot figure out why it isn't going to the correct method when I'm calling a POST on http://localhost:xxxx/api/chat/joingroup/1234.
SOLUTION:
The solution was to reference both values that are needed in the JoinGroup method, id and connectionID. Now the request will hit this method.
Using:
http://localhost:xxxx/api/chat/joingroup/john?connectionid=123 will work.
I noticed two things on the code you sent through:
the path you POST to is: localhost:xxxx/joingroup/1234 , this
should be localhost:xxxx/api/chat/joingroup/1234
because you have 2 parameters for the joingroup, you will need to pass both of them through, may be like this localhost:xxxx/api/chat/joingroup/1234?connectionID=value or you can pass it on the request body
if the connectionID is optional you can modify the method to use option al parameters like this
public string JoinGroup(string id, string connectionID = "")
please let me know if this helps.
Thanks
Ashraf
I assume the connectionID parameter references the POSTed data. The easiest thing to make it work is to decorate it with the [FromBody] attribute and put an = in front of the value being sent like this: =MyConnection1.
Web API expects an object with properties or an array otherwise. Alternatively, you can wrap the connection ID with a custom class and pass it serialized as JSON/XML.
Related
**web-api**
As u can see I have to develop different api . For this I have created a MasterController. But I am getting an problem. I am using Postman for testing and I am getting the following problem. Please help me to resolve this issue. I am getting multiple match points. Please help me resolve the issue.
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using VENUS.HRMS.API.Filters;
using VENUS.HRMS.DATA.Data;
using VENUS.HRMS.DATA.Models;
namespace VENUS.HRMS.API.Controllers
{
[ApiController]
[Route("[controller]")]
public class MasterController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet]
[AuthorizationFilter]
public IEnumerable<TblMstEmpRole> Get()
{
var emprole = new EmpRoleData().GetMstEmpRole();
return emprole;
}
[HttpGet]
[AuthorizationFilter]
public IEnumerable<TblMstState> GetState()
{
var state = new StateData().GetMstState();
return state;
}
[HttpGet]
[AuthorizationFilter]
public IEnumerable<TblMstCity> GetCity()
{
var city = new CityData().GetMstCity();
return city;
}
}
}
The issue is exactly what error says: The request matched multiple endpoints
Remember when calling an API, the function name does not matter. The function name is only for "Internal" use within your C# applicaiton. The name of the API endpoint correlates to it's route. This route is set with either the [Route()] attribute or within the [HttpGet()] attribute.
For example you have 3 functions:
[HttpGet]
public IEnumerable<TblMstEmpRole> Get()
[HttpGet]
public IEnumerable<TblMstState> GetState()
[HttpGet]
public IEnumerable<TblMstCity> GetCity()
The full route for these three functions respectively are:
Get --> /Master/
Get --> /Master/
Get --> /Master/
Yes all thee are the same. And that is the exact issue you are having. To fix this change the Route for your endpoints like this:
[HttpGet]
public IEnumerable<TblMstEmpRole> Get()
[HttpGet("State")]
public IEnumerable<TblMstState> GetState()
[HttpGet("City")]
public IEnumerable<TblMstCity> GetCity()
Now the routes will look like this:
Get --> /Master/
Get --> /Master/State
Get --> /Master/City
The rule is:
You can only have one route name per Http operation.
Different HTTP Methods
[HttpGet] is one of the attributes which has a GET method, however, there are many other methods such as POST, HEAD, PUT, OPTIONS, DELETE, CONNECT, TRACE and PATCH.
To utilize them through an API endpoint all you need to do is add it to the function. All the attributes have the [Http***] format. Example Post and Put will be:
[HttpPost]
[HttoPut]
And has mentioned a little above, you can only have one Route for each method. See examples below:
// PATH: GET --> /Master/
[HttpGet]
public IEnumerable<TblMstEmpRole> GETGet()
// PATH: POST --> /Master/
[HttpPost]
public IEnumerable<TblMstEmpRole> POSTGet()
The above example is VALID. Even though we have two functions with the same route, they do have different Http Methods and as such are uniquely identifiable.
Why Different Methods
Each method has it's own quirks and uses. Depending on the method, some things are allowed and some are not. When a client makes a query to a server and sends something, that is called a Http Request. The method for either getting or setting it is the Http Method, example GET, POST, PUT...
A Http Request has the following structure:
Headers
Parameters
Body
Footer
If we were to use the GET method, then you are telling the endpoint that it should ignore the Body of the request.
If we were to use the HEAD method then we are telling the endpoint that we are only interested in the Headers section of the request.
Now to figure out which to use and when can be tricky, but as a basic rule of thumb you can use this:
GET - You are only fetching data and not sending anything in the body
POST - When you are creating something in the database or sending data in the body
PUT - Similar to Post, but only when you are updating something
DELETE - When you are removing something.
These are the basic 4 any beginner to intermediate should be aware of. For more information on what method to use see: https://www.w3schools.com/tags/ref_httpmethods.asp
Hi every one I am new to Asp.net Web API and I had my question post data using asp.net Web API and got my answer accepted.
This is an extension to the same question I want to post data with some header value in the Postman and my code is as follows
public HttpResponseMessage PostCustomer([FromBody] NewUser userData, string devideId)
{
//My code
return response;
}
When I hit this in Postman passing values in JSON format in BODY - raw I got message as follows
No HTTP resource was found that matches the request URI
No action was found on the controller that matches the request.
Please help me.
It looks like you have added some additional devideId string parameter to your action. Make sure that you are supplying a value to it as a query string when making the request:
POST http://localhost:58626/api/customers?devideId=foo_bar
If you don't want to make this parameter required then you should make it optional (in terms of optional method parameter in .NET):
public HttpResponseMessage PostCustomer([FromBody] NewUser userData, string devideId = null)
{
...
}
Now you can POST to http://localhost:58626/api/customers without providing a value for this parameter.
Remark: You don't need to decorate a complex object type (such as NewUser) with the [FromBody] attribute. That's the default behavior in Web API.
UPDATE: Here's how you could read a custom header:
public HttpResponseMessage PostCustomer(NewUser userData)
{
IEnumerable<string> values;
if (this.Request.Headers.TryGetValues("X-MyHeader", out values))
{
string headerValue = values.FirstOrDefault();
}
...
return response;
}
I am using web api with identity 2.0.
I want to assign multiple roles to a user.
I am using post man client to communicate with web api.
In api the method i used to get user id and roles for the user is given below:
public async Task<IHttpActionResult> AddRoleToUser(string userid,[FromUri] string[] selectedRoles)
Here selectedRoles is an array.
From postman client i am passing array as given below :
I am passing user id in the url.
Am i passing the array of roles in correct format from postman client?
The call to api was success ,but selectedRoles always contains null value.
I tried with raw json as given below,but it did not worked
If i can pass the roles as raw json,can any show an example
First problem: you're specifying that selectedRoles array comes from the URI (URL) with this attribute: [FromUri]. You need to remove this attribute, because you're sending it in the request body, not in the URL.
Web API actions can only receive a single parameter from the request body, and any number of parameters from the URL. So you need to pass the userid parameter as a query string parameter, like this ?userid=123123, and the other parameter in the body. (You could also create a route that includes a userid, or receive the userid as the id parameter and pass it as an URL segment, if you're using the deafult route)
You also need to specify in your headers the format of the information you're sending in the body. So you need to include this header: Content-Type: application/json, because you're including JSON in your request body.
Finally, as you're sending a single parameter from the body, you can send it like this:
['Admin', 'Employee']
If you want to use the format in your example, you shuould create a class to use as parameter in your action, that would look like this:
public class RoleList
{
public string[] selectedRoles { get; set; }
}
And your action should include this as parameter:
public async Task<IHttpActionResult>
AddRoleToUser(string userid, RoleList roleList)
method code in C#:
public void Put([FromBody]string[] selectedRoles)
{
foreach (var role in selectedRoles)
{
}
}
to call method in postman
in heading tab add your parameter and Content-type parameter with value application/x-www-form-urlencoded
in body tab select raw and JSON(application/json)
then in body box add your array like below:
['Admin', 'Employee']
I'm trying to pass in an URL as a string parameter to a WEB API GET method.
The controller:
public class LinksController : ApiController
{
public HttpResponseMessage Get(string targetUrl)
{
//query db with targetURL
}
}
The aim is to query the database to see if the URL is stored. This works fine with simple URLs and URLs whose query string contains a single parameter, like:
http://www.youtube.com/watch?v=nLPE4vhSBx4
The problem I'm encountering is specifically when the query string contains multiple parameters, e.g.
http://www.youtube.com/watch?v=nLPE4vhSBx4&feature=youtube_gdata
When debugging, the value of targetUrl is only ".../watch?v=nLPE4vhSBx4" which means &feature=youtube_gdata is lost.
The GET request looks like this:
http://localhost:58056/api/links?targetUrl=http://www.youtube.com/watch? v=nLPE4vhSBx4&feature=youtube_gdata
I've also tried to add the following route in WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "Links",
routeTemplate: "api/links/{targetUrl}",
defaults: new { controller = "Links", targetUrl= RouteParameter.Optional }
);
But the GET request then results in 400 Bad Request.
So my question is, can't this be done? I would like the complete URL! Or would I need to change the method header to use the [FromBody] attribute and pass it as a JSON object?
You should URLEncode your target URL parameter so that it doesn't get mistaken for subsequent query string parameter. This means the URL you specified should appear as:
http://localhost:58056/api/links?targetUrl=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DnLPE4vhSBx4%26feature%3Dyoutube_gdata
And then inside your Get method, URLDecode the string that is passed as a parameter.
Both methods can be found in System.Web.HttpUtility
I'm new to ASP.NET Web API but I'm hitting an issue where if I'm trying to add two get methods to a single controller it is failing at trying to get the action. Are we allowed only one get per controller, unless we are adding an overload of the same method?
This works if I only have one get method, e.g. the first one. As soon as I add Get to the second method name, it gives me a 500.
[HttpGet]
public string GetToday()
{
return "Hello today";
}
[HttpGet]
public string GetPending()
{
return "Hello Pending";
}
The calls I'm making:
http://abc.com/api/tasks/gettoday
http://abc.com/api/tasks/getpending
I can make this call if the method name is just 'Today', as long as I put the [HttpGet] attribute. But only if I don't have the second action. Which means if I remove the [HttpGet] attribute from the second method as well as remove 'Get' from the method name so it is just Pending, then it works.
As soon as there are two get methods I get this error:
{"Message":"An error has occurred.","ExceptionMessage":"Multiple actions were found that match the request: \r\nSystem.String TodaysTasks() on type TaskTrackerService.Controllers.TasksController\r\nSystem.String PendingTasks() on type TaskTrackerService.Controllers.TasksController","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)"}
The idea behind a Web API is that it represents a ReSTful resource. The semantics of ReST imply that a controller corresponds to the idea of a 'resource' and that behaviour is determined by the HTTP verb used to access the resource — means GET, POST, PUT, DELETE, …
The model used for these resources is that you treat them somewhat "CRUD-y"; that you query for elements of the resource, put new elements into the resource, update elements in the resource – you get the idea.
For each (relevant) verb you should have only one action that deals with requests of that nature. The idea of having multiple actions accessed by GET is not very ReSTful and more a MVC-ish RPC-style mode of thinking that is not well-suited for ASP.NET Web API.
So your controller might instead be written:
public class FooController : ApiControlle
{
[HttpGet]
public Account Get(string flavour)
{
switch (flavour) {
case "greeting": return "Hello today";
case "pending": return "Hello Pending";
default: throw new InvalidOperationException("Cannot get your flavour of foo :(");
}
}
}
But as you have written in a comment, the trouble was that you never intended to use the controller as Web API controller, but MVC controller.