I have a URL https://localhost/api/Books/{value} using attribute routing , but whenever I pass {value} as null or empty string WEB API is not able to route it to the desired controller? How to go about this issue and resolve it.Error message details is : No action was found on the controller 'Books' that matches the request."
Assuming you've registered the default route, a request to...
http://localhost/api/books/34
will be handled by the followung controller/action without even using attribute routing...
public BooksController : ApiController
{
public IHttpActionResult Get(int id){...}
}
however, if you need attribute routing you decorate your action method with the following attribute...
[HttpGet, Route("api/books/{id}")]
public IHttpActionResult Get(int id){...}
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.
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid:int?}")]
public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}
Reference: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#optional
Or a better approach (if that works in your scenario) would be to have 2 different routes:
[Route("api/books/{id}")]
public BooksController : ApiController
{
public IHttpActionResult Get(int id){...}
}
[Route("api/books")]
public BooksController : ApiController
{
public IHttpActionResult Get(){...}
}
Related
Before I ask a question I should say that; All routes have been added to the controllers by Route attribute. It's not a duplicate of this or this. Because ID parameter (integer type) is passed to the two different functions in this case.
There are two classes and two functions that all seperated in different class. HomeController.AppPage and BlogController.Detail functions conflicts when this page localhost:11111/Blog/this-is-blog-title/1 is navigated. I want to run Second One as I stated below.
In the Second One, Blog segment must be stable in the beginning of the route. I don't want to change or remove.
Thank you for your suggestion and help.
First One
public class HomeController : BaseController
[Route("{title}/{ID}")] // -> No problem with this
[Route("{title1}/{title2}/{ID}")] // -> Conflicting attribute
public ActionResult AppPage(int ID)
{
// Some Code
return View();
}
}
Second One
public class BlogController : BaseController
[Route("Blog/{title}/{ID}")] // -> Conflicting attribute
public ActionResult Detail(int ID)
{
// Some Code
return View();
}
}
try adding order parameter to the route attribute so that the Blog route takes precedence over the title1 route
By default, all defined routes have an Order value of 0 and routes are processed from lowest to highest
public class HomeController : BaseController
[Route("{title}/{ID}")] // -> No problem with this
[Route("{title1}/{title2}/{ID}", Order = 2)]
public ActionResult AppPage(int ID)
{
// Some Code
return View();
}
}
public class BlogController : BaseController
[Route("Blog/{title}/{ID}", Order = 1)]
public ActionResult Detail(int ID)
{
// Some Code
return View();
}
}
if this did not work you can just list them in the RouteConfig.cs file and write the Blog route before the title1 route
you can read this article for more info
http://rion.io/2015/11/13/understanding-routing-precedence-in-asp-net-mvc/
I have a controller named HomeController, I am re-writing its all actions and even the name of controller,by decorating them with Route and Routeprefix respectively.
This is my controller with action method Index
[RoutePrefix("MyPortal")]
public class HomeController : Controller
{
[Route("Home")]
public ActionResult Index()
{
}
[Route("Index")]
public ActionResult LandingPage()
{
}
}
Its working fine,but when I type Home/Index in URL,its giving me error
A public action method 'index' was not found on controller 'Web.Controllers.HomeController'. /Home/index
I want to redirect the user to Index ActionResult,if he type Home/Index or MyPortal/Home
Similarly, he should redirect to LandingPage, if he type Home/LandingPage or MyPortal/Index.
MyPortal/Home or MyPortal/Index is working fine.
How about this one ..? But your last case (MyPortal/Index) will not work
[RoutePrefix("Home")]
[RoutePrefix("MyPortal")]
public class HomeController : Controller
{
[Route("Index")]
[Route("Home")]
public ActionResult Index()
{
}
[Route("LandingPage")]
public ActionResult LandingPage()
{
}
}
I have a website developed in MVC 5, I'm using route attributes for routing.
I've set the default controller and the default action for each controller using the following code
public class CompanyController : MainController
{
[Route("~/", Name = "default")]
[Route("Company/Index")]
public ActionResult Index(string filter = null)
{
//My code here
}
[Route("Company/Edit")]
public ActionResult Edit(int id)
{
//My code here
}
}
I've another controller with a default action :
[RoutePrefix("Analyst")]
[Route("{action=Index}")]
public class AnalystController : MainController
{
[Route("Analyst/Index")]
public ActionResult Index(string filter = null)
{
//My code here
}
[Route("Analyst/Edit")]
public ActionResult Edit(int id)
{
//My code here
}
}
The default controller worked perfectly, but when I navigate to the analyst controller without specifying the name of the action I get the following error:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
SurveyWebsite.Controllers.AnalystController
SurveyWebsite.Controllers.CompanyController
How can I correct navigate to http://localhost:61534/analyst and reach the default action ( index) ? The action also should remain accessible by http://localhost:61534/analyst/Index
Thanks for your help.
Give an empty string as the route value for index action so that it works for Analyst, which is your controller route prefix. You can decorate with a second Route attribute for it to work with "Analyst/Index" url where you will pass "Index" to it.
[RoutePrefix("Analyst")]
public class AnalystController : MainController
{
[Route("")]
[Route("Index")]
public ActionResult Index(string filter = null)
{
//My code here
}
[Route("Edit/{id}")]
public ActionResult Edit(int id)
{
//My code here
}
}
This will work for both /Analyst and /Analyst/Index
Code:
[RoutePrefix("api/v1/portfolio")]
public class PortfolioController : ApiController
{
[Route("")]
[Route("index")]
public IEnumerable<PortfolioModel> GetPortfolios()
{
...
}
[Route("{id:int}")]
[ResponseType(typeof(PortfolioModel))]
public IHttpActionResult GetPortfolio(int id)
{
....
}
[Route("{id:int}/credentials")]
[ResponseType(typeof(IEnumerable<CredentialsModel>))]
private IEnumerable<CredentialsModel> GetCredentialsForId(int id)
{
....
}
}
api/v1/portfolio works
api/v1/portfolio/index works
api/v1/portfolio/1 works
api/v1/portfolio/1/credetials DOES NOT WORK
getting:
HTTP Error 404.0 - Not Found. The resource you are looking for has been removed
Can any of you gurus tell me what I'm doing wrong?
In case you ask, my WebApiConfig only has
config.MapHttpAttributeRoutes();
and my RegisterRoutes has only:
routes.MapMvcAttributeRoutes();
as always, thank you for the help.
change it from private to public
private IEnumerable GetCredentialsForId(int id)
to
public IEnumerable GetCredentialsForId(int id)
it will work!
I finally solved the problem. I'll explain for others having similar problem:
As you see in the previous code, I have the action (returning credentials) in the portfolio controler, instead I use similar action in my Credentials controler and map routed the action as if coming from the portfolio controler.
[Route("~/api/v1/portfolio/{id}/credentials")]
[ResponseType(typeof(IEnumerable<CredentialsModel>))]
public IEnumerable<CredentialsModel> GetByPortfolioId(int id)
{
return _repository.FindByPortfolioId(id);
}
so, I have to map to the controler in charge of returning a particular model. My problem was I had an action returning CredentialModel in a controler in charge of handling PortfolioModel.
Is it "legal" to have a controller inherit a route from its BaseController ? It seems it's not allowed for Attribute Routing , but how about normal route registration via RouteCollection?
The reason is I currently have a bunch of controllers, each representing some kind of file converter. Each of them has a common set of methods to upload the file to be converted. These method are endpoints on each controller not just private methods. I'd like for the following routes to be valid:
/api/controller1/uploadfile
/api/controller2/uploadfile
/api/controller3/uploadfile
Can I get an example how this could be done inside a BaseController and if it's not possible, an alternative.
Here's what works:
public abstract class BaseUploaderController : ApiController
{
[HttpGet, Route("uploadfile")] //Needs both because HttpGet("uploadfile") currently only supported in MVC attribute routing
public string UploadFile()
{
return "UploadFile";
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseUploaderController
{
[Route("{id:int}")]
public string Get(int id)
{
return "value";
}
}
Are you looking to place this UploadFile action in the base controller and other controllers inheriting from them should still be able to hit UploadFile from their respective routes like you mentioned in your post? If yes, you could create an abstract base api controller and place this UploadFile action in it and your requests to the individual controllers should work as expected.
Example:
public abstract class BaseApiController : ApiController
{
// POST /api/Values
// POST /api/Test
public string UploadFile()
{
return "UploadFile";
}
}
public class TestController : BaseApiController
{
// GET /api/test/10
public string GetSingle(int id)
{
return "Test.GetSingle";
}
}
public class ValuesController : BaseApiController
{
// GET /api/values/10
public string GetSingle(int id)
{
return "Values.GetSingle";
}
}
As per this answer https://stackoverflow.com/a/21610390/122507 attribute routes are not inherited.
I am currently debating between introducing unnecessary method in 30 controllers just so I can add an attribute route or add a fake parameter to the base class method to let the default routing disambiguate between Get(int id) and GetHistory(int id, bool history) where I don't need the second parameter.