Why was no action found for route? - asp.net-web-api

My WebApiConfig.cs contains just this one route:
config.Routes.MapHttpRoute(
name: "Calendar",
routeTemplate: "api/Calendar/{date}",
defaults: new { controller = "Calendar", action = "Get", date = RouteParameter.Optional }
);
And my CalendarController class has only this method:
public IEnumerable<FVEvent> Get( string dateStr )
{
return db.Events.ToList();
}
But when I try hitting /api/Calendar in the browser, I get the following MessageDetail in the XML:
No action was found on the controller 'Calendar' that matches the request.
Plus I don't know why it's XML, I'd like it to be JSON.
There's just so much hidden magic going on I can't make a slight adjustment to a simple example without everything falling apart.

Your route marks the dateStr parameter as optional, but the method signature does not. Try adding a default value to your dateStr parameter:
public IEnumerable<FVEvent> Get(string dateStr = "")
{
return db.Events.ToList();
}

Make sure the name of your Calendar Controller ends with Controller. Also use the following code in your WebAPI config to remove the XML formatter.
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

Related

MVC Web API Routing to Wrong Action

I have a web API controller, when I call the default Get action this works, when I call another specific action (GetReservationsForCustomer) this also works, but one action gives an error (GetReservationsByDate), it seems to route to the default Get action. Here is the code:
// GET: api/Reservations
public IQueryable<Reservation> GetReservations()
{
return db.Reservations;
}
[ResponseType(typeof(ReservationDTO))]
public IHttpActionResult GetReservationsForCustomer(int CustomerId)
{
IEnumerable<Reservation> reservations = db.Reservations.Where(r => r.CustomerId == CustomerId).ToList();
List<ReservationDTO> reservationList = new List<ReservationDTO>();
foreach(Reservation reservation in reservations)
{
reservationList.Add(new ReservationDTO
{
id = reservation.id,
ReservationStart = reservation.ReservationStart,
Covers = reservation.Covers
});
}
return Ok(reservationList);
}
[ResponseType(typeof(ListReservationDTO))]
public IHttpActionResult GetReservationsByDate(DateTime StartDate, DateTime EndDate)
{
IEnumerable<Reservation> reservations = new List<Reservation>();
if (EndDate != null)
{
reservations = db.Reservations.Where(r => r.ReservationStart.Date >= StartDate.Date && r.ReservationStart.Date >= EndDate.Date).ToList();
}
else
{
reservations = db.Reservations.Where(r => r.ReservationStart.Date == StartDate.Date).ToList();
}
List<ReservationDTO> reservationList = new List<ReservationDTO>();
foreach (Reservation res in reservations)
{
reservationList.Add(new ReservationDTO
{
id = res.id,
ReservationStart = res.ReservationStart,
Covers = res.Covers,
CustomerEmail = res.Customer.EmailAddress,
CustomerName = res.Customer.Name,
CustomerPhone = res.Customer.PhoneNumber
});
}
return Ok(reservationList);
}
Here is my API call:
http://localhost:55601/api/Reservations/GetReservationsByDate/?StartDate=2018-03-04:T12:30:00
And here is the response:
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Http.IHttpActionResult GetReservation(Int32)' in 'GreenLionBookings.API.ReservationsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
Please note the specifics of the action are not relevant at this stage, I've butchered it a fair bit trying to get this to work! I've tried specifying a start date and end date and neither seems to work. It always seems to get routed to the default Get action.
Here is my RouteConfig:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
And here is my 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 }
);
}
So both basically just default.
Why is this not routing to the correct action, just for this one and not the others? I have another controller (Customers) which seems to work correctly for all actions too. I've read this and this, and also this and this which I actually thought was pretty relevant and quite helpful, but didn't solve my problem.
What am I doing wrong here?
First of all, you have a typeo in the date.
This 2018-03-04:T12:30:00 should be 2018-03-04T12:30:00.
Then, to solve the routing problem, you could leave out the action name of the url and let the framework match the request against the parameters name.
Try it like this
api/Reservations?StartDate=2018-03-04T12:30:00&EndDate=2018-03-05T12:30:00
Then, if you want to be able to send nullable values to EndDate which is a value type of DateTime; make the DateTime nullable
[ResponseType(typeof(ListReservationDTO))]
public IHttpActionResult GetReservationsByDate(DateTime StartDate, DateTime? EndDate)
Notice the DateTime? which is a shorthand for Nullable<DateTime>

Web Api route configNot Working

I have these 2 configs:
config.Routes.MapHttpRoute(
name: "CustomRate1",
routeTemplate: "api/CustomRate/{action}/{idCustomRate}",
defaults: new { controller = "CustomRate" }
);
config.Routes.MapHttpRoute(
name: "CustomRate",
routeTemplate: "api/CustomRate/{action}/{idCountry}",
defaults: new { controller = "CustomRate" }
);
However, only one of them works at a time. If I put CustomRate before the route works, but CustomRate1 does not, the reverse also happens, if I put CustomRate1 on top CustomRate does not work. Any suggestions
Currently in my controller I have
[HttpPost()]
[HttpOptions]
public CustomRateListReturnType GetCustomRateListByCountry(long idCountry)
{
}
[HttpPost()]
[HttpOptions]
public BaseReturnType DesactivateCustomRate(long idCustomRate)
{
}
I am currently using these two calls
http://127.0.0.1/quotesystemserver/api/CustomRate/GetCustomRateListByCountry/5
and
http://127.0.0.1/quotesystemserver/api/CustomRate/DesactivateCustomRate/3
Ok What I did is as follows:-
My config is now as follows:-
config.Routes.MapHttpRoute(
name: "CustomRate1",
routeTemplate: "api/CustomRate/{action}/{id}",
defaults: new { controller = "CustomRate" }
);
My controller contains:-
[HttpPost()]
[HttpOptions]
public CustomRateReturnType GetCustomRateListByCountry(long id)
{
}
[HttpPost()]
[HttpOptions]
public BaseReturnType DesactivateCustomRate(long id)
{
}
Just have one route.
The placeholders {action} and {id} will take the values what you pass in the URL.
If you call http://127.0.0.1/quotesystemserver/api/CustomRate/GetCustomRateListByCountry/5
,the route will take action as GetCustomRateListByCountry and id as 5.
Whatever you give the name to the placeholder { } doesnot matter. It is just the placeholder name. The value comes from the URL.
use attribute routing, it gives more clearer picture of the custom routes for any person who is developing the application for rest you can use config routes:
Decorate your controller with
[RoutePrefix("api/CustomRate")]
And your action methods as follows
HttpPost()]
[HttpOptions]
[Route("GetCustomRateListByCountry/{idCountry:long}]
public CustomRateListReturnType GetCustomRateListByCountry(long? idCountry)
{
}
[HttpPost()]
[HttpOptions]
[Route("DesactivateCustomRate/{idCustomRate:long}]
public BaseReturnType DesactivateCustomRate(long? idCustomRate)
{
}
Add the below code in webapiconfig.cs
config.MapHttpAttributeRoutes();

Can I have non Rest styled Methods in web api?

My question specifically is if i can support something like this
public class SomeTestController : ApiController
{
[System.Web.Http.AcceptVerbs("GET")]
public SomeInfo GetSomeBlah(int a, int b)
{
return new SomeInfo
{
Name = string.Format("GetSomeBlah - {0}", a),
CompanyID = b.ToString()
};
}
[System.Web.Http.AcceptVerbs("GET")]
public SomeInfo GetSomeBlah1(int a, int b)
{
return new SomeInfo
{
Name = string.Format("GetSomeBlah1 - {0}", a),
CompanyID = b.ToString()
};
}
}
I tried to add the following route
config.Routes.MapHttpRoute(
name: "DefaultApi4",
routeTemplate: "api/{controller}/{action}",
defaults: new {action = RouteParameter.Optional}
);
Edit: 4/7/15
I forgot to mention it should also work with rest styled methods.
Not sure why you are trying to do this, it sounds like you have misunderstood how routing works - Have you seen these tutorials on Routing and how ASP.net maps routes?
From point 3b below this means that if you name your input varibles more meaningfully, the framework will be able to map on the name and type coming in from the route as to which method to pick i.e. getSomeBlah or GetSomeBlack1
Summary for others,
1). Create a list of all actions on the controller that match the HTTP request method.
2). If the route dictionary has an "action" entry, remove actions whose name does not match this value.
3). Try to match action parameters to the URI, as follows:
a). For each action, get a list of the parameters that are a simple type, where the binding gets the parameter from the URI. Exclude optional parameters.
b). From this list, try to find a match for each parameter name, either in the route dictionary or in the URI query string. Matches are case insensitive and do not depend on the parameter order.
c). Select an action where every parameter in the list has a match in the URI.
d). If more that one action meets these criteria, pick the one with the most parameter matches.
4). Ignore actions with the [NonAction] attribute.
That will work if you remove the default route. I also indicated where the parameters are from.
Specify the input is from the URL:
public SomeInfo GetSomeBlah([FromUri] int a, [FromUri] int b)
Comment or remove this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Test URL:
http://localhost:64719/api/Values/GetSomeBlah?a=1&b=2
Response:
<ValuesController.SomeInfo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/SampleMVC.Controllers"> <CompanyID>2</CompanyID><Name>GetSomeBlah - 1</Name> </ValuesController.SomeInfo>
configure it like this:
config.Routes.MapHttpRoute("DefaultApi2",
"apiws/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute("DefaultApi",
"apiws/{controller}/{id}",
new { id = RouteParameter.Optional });
this works for both rest style and non-rest styles apis.
Note that the sequence of the two statements matters.
I managed to get it to work with the following code
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and Attribute routing.
[RoutePrefix("api/someother")]
public class SomeOtherController : ApiController
{
// GET: api/SomeOther
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET: api/SomeOther/5
public string Get(int id)
{
return "value";
}
// POST: api/SomeOther
public void Post([FromBody]string value)
{
}
// PUT: api/SomeOther/5
public void Put(int id, [FromBody]string value)
{
}
[System.Web.Http.HttpGet()]
[System.Web.Http.Route("Findsomething2")]
public CompanyInfo Findsomething2(int id, int b)
{
return new CompanyInfo
{
CompanyID = b.ToString(),
Name = id.ToString() + " 2"
};
}
// DELETE: api/SomeOther/5
public void Delete(int id)
{
}
}

Web API Help is not generated as expected

[RoutePrefix("Office")]
public class OfficeController : ApiController
{
[Route("~/Admin/{adid}")]
public string GetAdminById(int adid)
{
return string.Format("Enter value is {0}", adid);
}
[Route("~/User/{uid}")]
public string GetUserByName(int uid)
{
return string.Format("Enter value is {0}", uid);
}
}
but help is generated below
GET api/Office?adminid={adid}
GET api/Office?userid={uid}
but I am excepting
GET Office/Admin/adid
GET Office/User/uid
For this I added one more route, for admin at webapi.config
config.Routes.MapHttpRoute(
name: "OfficeadminApi",
routeTemplate: "api/{controller}/Admin/{adid}",
defaults: new { **adid** = RouteParameter.Optional }
);
then 2 signatures are generated by web api help are
GET api/Office?adid={adid}
GET api/Office/Admin/{adid} // I expected is generated but
what is the use of Attribute routing, if added no'f Routes at webapi.config?
as per Attribute routing it should generates, routes specified at attribute level.
Please clarify on this any one please ?

ASP.Net MVC: Passing a string parameter to an action using RedirectToAction()

I would like to know how to pass a string parameter using RedirectToAction().
Let's say I have this route:
routes.MapRoute(
"MyRoute",
"SomeController/SomeAction/{id}/{MyString}",
new { controller = "SomeController", action = "SomeAction", id = 0, MyString = UrlParameter.Optional }
);
And in SomeController, I have an action doing a redirection as follows:
return RedirectToAction( "SomeAction", new { id = 23, MyString = someString } );
I tried this redirection with someString = "!##$%?&* 1" and it always fails, no matter if I encode the string. I tried encoding it with HttpUtility.UrlEncode(someString), HttpUtility.UrlPathEncode(someString), and with Uri.EscapeUriString(someString) to no avail.
So I resorted to us TempData to pass someString, but still, I would be curious to know how to make the code above work, just to satisfy my curiosity.
I think the issue might be either in your route order, or in your controller. Here is some code that I got to work.
Route Definitions
routes.MapRoute(
"TestRoute",
"Home/Testing/{id}/{MyString}",
new { controller = "Home", action = "Testing", id = 0, MyString = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
// note how the TestRoute comes before the Default route
Controller Action Methods
public ActionResult MoreTesting()
{
return RedirectToAction("Testing", new { id = 23, MyString = "Hello" });
}
public string Testing(int id, string MyString)
{
return id.ToString() + MyString;
}
When I browse to /Home/MoreTesting I get the desired output of "23Hello" to output in my browser. Can you post your routes and your Controller code?
OK, I know this question is a few days old but I wasn't sure if you got this issue sorted or not so I had a look. I played around with this for a while now and this is what the problem is and how you could solve it.
The problem you are having is that the special characters causing issues are one of the many (I think 20) special characters, such as % and ".
In your example the problem is the % character.
As pointed out by Priyank here:
The route values are posted as part of the URL string.
The Url string (not query string parameter) can't handle %(%25), "(%22) and so on.
Further, as pointed out by Lee Gunn in the same post:
http://localhost:1423/Home/Testing/23/!%40%23%24%25%3f%26*%201 - (this will blow up)
One of the ways to fix this is to remove {MyString} from the route mapping. To make your root mapping look like this:
routes.MapRoute(
"TestRoute",
"Home/Testing/{id}",
new { controller = "Home", action = "Testing", id = 0, MyString = UrlParameter.Optional }
);
This will cause the post to generate this:
http://localhost:1423/Home/Testing/23?MyString=!%2540%2523%2524%2525%2B1
Now when you set MyString it will be turned into a query string parameter which works perfectly fine.
I did try that and it did work.
Priyank also mentioned in the SO post I linked above that you maybe could solve this with a custom ValueProvider but you would have to follow his linked article their to check if that is something that would apply to you.

Resources