Route Attribute not working in Web API 2 - asp.net-web-api2

I have two GET methods on my API controller. When I attempt to call the GetByCompanyId method, which I have decorated with the Route Attribute, the request instead is being routed to the GetById method. Below are the relevant code files.
global.ascx.cs
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
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.EnableCors();
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
route.config
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 }
);
}
}
CompanyFunctionsController.cs
public IHttpActionResult GetById(int id)
{
var companyFunction = this._service.GetByKey(new object[] { id });
if (companyFunction != null)
{
var companyFunctionDto = Mapper.Map<CompanyFunctionDto>(companyFunction);
return Ok(companyFunctionDto);
}
return NotFound();
}
[Route("CompanyFunctions/GetByCompanyId", Name = "GetByCompanyId")]
[HttpGet]
public IEnumerable<CompanyFunctionDto> GetByCompanyId(int id)
{
var collection = this._service.GetAll().ToList().Where(x => x.CompanyId == id);
IEnumerable<CompanyFunctionDto> collectCompanyFunctionDtos = Mapper.Map<IEnumerable<CompanyFunctionDto>>(collection);
return collectCompanyFunctionDtos;
}
My HTTP request:
http://localhost:1317/api/CompanyFunctions/GetByCompanyId?id=1

If you want to have a route that starts with api like http://localhost:1317/api/CompanyFunctions/GetByCompanyId?id=1 then you must use the string api in your route attribute that you want it to go to.
[Route("api/CompanyFunctions/GetByCompanyId", Name = "GetByCompanyId")]
Otherwise it will only match based on the http verb (Get in this case).
Alternatively you can decorate the web api controller with the [RoutePrefix("api/CompanyFunctions")] attribute as well and change your Route attribute to [Route("GetByCompanyId", Name = "GetByCompanyId")]

Web API 2 supports a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API
You might use Attribute Routing in Web Api to solve your problems. Your Controller Action should be like this,
[Route("CompanyFunctions/GetByCompanyId/{companyId}"]
public IEnumerable<CompanyFunctionDto> GetByCompanyId(int companyId)
{
var collection = this._service.GetAll().ToList().Where(x => x.CompanyId == companyId);
IEnumerable<CompanyFunctionDto> collectCompanyFunctionDtos = Mapper.Map<IEnumerable<CompanyFunctionDto>>(collection);
return collectCompanyFunctionDtos;
}
and your HTTP request is http://localhost:1317/CompanyFunctions/GetByCompanyId/1

Related

Defalut routing not working after adding route attribute in asp.net mvc

I created an mvc project with no attribute route. It was working fine. When I added an attribute route [Route("employeehome")] on one of my action methods, the conventional routing {controller}/{action}/{id} ie employee/index for that method ceased working. Is there anything wrong with my approach?
Adding my code snippet.
Route Config Code.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Controller code.
public class EmployeeController : Controller
{
// GET: Employee
[Route("employeehome")]
public ActionResult Index()
{
return View();
}
}

ASP MVC default route is not applying for root site url

I'm having trouble getting ASP.net MVC to serve up the default controllers index view for the root site url http://mysite:8080/. All I get back is a 404 with The resource cannot be found. It works fine if I specify the full route path in the url : http://mysite:8080/Default/Index, however, I want the default route to apply even if the user doesn't specify the route path though. It seems that this should just work out of the gate. This is a fresh project from the VS2013 MVC 4 template.
I've tried both route mappings below and neither seems to work. How can this be achieved?
routes.MapRoute(
"Root",
"",
new { controller = "DefaultController", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "DefaultController", action = "Index", id = UrlParameter.Optional }
);
Here is a solution to this problem. It's disappointing that the default route's defaults do not apply for the root site url though.
routes.Add(new Route("", new SiteRootRouteHandler("~/Default/Index")));
public class SiteRootRouteHandler : IRouteHandler
{
private readonly string _redirectUrl;
public SiteRootRouteHandler(string redirectUrl)
{
_redirectUrl = redirectUrl;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new SiteRootHandler(_redirectUrl);
}
}
public class SiteRootHandler: IHttpHandler
{
private readonly string _redirectUrl;
public SiteRootHandler(string redirectUrl)
{
_redirectUrl = redirectUrl;
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.RedirectPermanent(_redirectUrl);
}
}

Can I apply a MessageHandler to certain attribute routes?

I know you can assign a handler using convention-based routing, e.g:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var tokenHandler = new TokenValidationHandler()
{
InnerHandler = new TokenValidationHandler()
{
InnerHandler = new HttpControllerDispatcher(config)
}
};
config.Routes.MapHttpRoute(
name: "someApi",
routeTemplate: "v1/{controller}/{id}",
defaults: new object { id = RouteParameter.Optional },
constraints: null,
handler: tokenHandler
);
}
}
Is it possible to do this with attribute routing instead? What I'm trying to do is restrict some routes with a bearer token but allow other routes to allow anonymous access. Should I be using Filtering instead?
I'm interested in this also, I want a global message handler that doesn't apply to certain request types or routes to a particular controller (similar to stonetip).
Currently I'm just bypassing all logic in my handler if the request is of that type or for that controller, however I don't think this is the most efficient method:
public class MyHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method != HttpMethod.Get)
{
System.Web.Http.Routing.IHttpRouteData routeInfo = request.GetRequestContext().RouteData;
object objController = routeInfo.Values["controller"];
if (objController == null || objController.ToString() != "excluded")
{
//handler code for all non-excluded routes

Add Web Api controllers to an existing ASP.NET 4 web application

Following the steps from this question How to add Web API to an existing ASP.NET MVC 4 Web Application project? , I have added web api support to my application.
In my original scenario I have the following web mvc controller:
public class FranchiseController : Controller
{
public ActionResult List()
{
return View();
}
[DataContext]
public ActionResult GetAllFranchises()
{
var franchiseInfoViewModelList = new List<FranchiseInfoViewModel>();
var franchiseInfoList = _franchiseService.GetAll();
foreach (var franchiseInfo in franchiseInfoList)
{
franchiseInfoViewModelList.Add(new FranchiseInfoViewModel(franchiseInfo, p => p.IsImportant));
}
var jsonNetResult = new JsonNetResult
{
Formatting = Formatting.Indented,
Data = franchiseInfoViewModelList
};
return jsonNetResult;
}
}
When the user navigates to the List view, I am calling
$.getJSON("/franchise/GetAllFranchises")
.done(function (data) {
});
to go to the GetAllFranchises action method and return the json data. So far so good.
I have created the following web api controller:
public class FranchiseController : ApiController
{
public IEnumerable<FranchiseInfoViewModel> GetAllFranchises()
{
var allFranchises = new List<FranchiseInfoViewModel>();
var franchiseInfoList = _franchiseService.GetAll();
foreach (var franchiseInfo in franchiseInfoList)
{
allFranchises.Add(new FranchiseInfoViewModel(franchiseInfo, p => p.IsImportant));
}
return allFranchises;
}
}
and I am trying to get to its action method like this:
$.getJSON("api/franchise")
.done(function (data) {
});
I am getting 404 error and the app is trying to reach the following url:
/Franchise/api/franchise
instead of api/franchise.
Global Asax:
protected void Application_Start()
{
Log.StartSession();
ElmahExtension.SetCurrentApplication(this);
ViewEngines.Engines.Add(new OmegaViewEngine());
AreaRegistration.RegisterAllAreas();
SerializerConfig.Register(GlobalConfiguration.Configuration);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
Bootstrapper.Initialise();
FluentValidationModelValidatorProvider.Configure();
}
Default route:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
In my Controllers folder I have my web mvc controller:
Controller\FranchiseController
and I have made a new folder WebAPI to hold my web api controller
Controller\WebAPI\FranchiseController
What am I doing wrong ? Thanks!
I'm not sure if it's the right move to name "FranchiseController" both to the MVC Internet Application Controller and to the MVC Web API Controller (totally different things). After renaming one of them I think you should put the Web Api Controller in the root of the directory (Controller).

How to create ASP.NET Web API Url?

In ASP.NET MVC, we have #Url.Action for actions. Is there something similar like #Url.Api which would route to /api/controller?
The ApiController has a property called Url which is of type System.Web.Http.Routing.UrlHelper which allows you to construct urls for api controllers.
Example:
public class ValuesController : ApiController
{
// GET /api/values
public IEnumerable<string> Get()
{
// returns /api/values/123
string url = Url.Route("DefaultApi", new { controller = "values", id = "123" });
return new string[] { "value1", "value2" };
}
// GET /api/values/5
public string Get(int id)
{
return "value";
}
...
}
This UrlHelper doesn't exist neither in your views nor in the standard controllers.
UPDATE:
And in order to do routing outside of an ApiController you could do the following:
public class HomeController : Controller
{
public ActionResult Index()
{
string url = Url.RouteUrl(
"DefaultApi",
new { httproute = "", controller = "values", id = "123" }
);
return View();
}
}
or inside a view:
<script type="text/javascript">
var url = '#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "values", id = "123" })';
$.ajax({
url: url,
type: 'GET',
success: function(result) {
// ...
}
});
</script>
Notice the httproute = "" route token which is important.
Obviously this assumes that your Api route is called DefaultApi in your RegisterRoutes method in Global.asax:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
It works with the simpler form of Url.Action thus you don't have to reference any Routing names:
Url.Action("ActionName", "ControllerName", new { httproute = "DefaultApi" })
You might want to add an area = "" if the URL is needed within an Area. (Api controllers are outside of Areas by default.) I'm using MVC 4.
Want to be able to generate links in a typesafe manner, without hardcoded strings (controller names)?
There's a nuget for that! (and it's written by Mark Seeman)
https://github.com/ploeh/Hyprlinkr
Works like this:
Routes, as usual:
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
Get an URL:
var linker = new RouteLinker(request);
var uri = linker.GetUri<FooController>(r => r.GetById(1337));
Result:
http://localhost/api/foo/1337
Here is the KISS method for answering the question:
If this is the code that you would use to create a MVC controller URL
#Url.Action("Edit", "MyController")
In order to get a URL for the API version of the controller (assuming you use the same controller name) you can use
#Url.Action("Edit", "api/MyController")
All the Url.Action method is doing is appending the root path of the application, with the controller name, followed by the action name (unless it is "Index" in which case it is not appended. if the route values object has an id property the value is also appended to the URL.