Route Constraint Isnt working for ASP.NET MVC - asp.net-mvc

Why would the route http://localhost:2222/2012-adidas-spring-classic/37, not get picked up from the below route match? I get a 404 error.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*vti_inf}", new { vti_inf = #"(.*/)?_vti_inf.html(/.*)?" });
routes.IgnoreRoute("{*vti_rpc}", new { vti_rpc = #"(.*/)?_vti_rpc(/.*)?" });
#region API
routes.MapRouteLowercase(
"NamedHomeEvent",
"{year}-{name}/{Id}",
new { controller = "Event", action = "Index", year = DateTime.Now.Year },
new { year = #"\d{4}", Id = #"\d+" }
);
public virtual ActionResult Index(int? id, int? year, string name)
{

The routing engine cannot help you here. You could write a custom route to handle this case:
public class MyRoute : Route
{
public MyRoute()
: base(
"{year-name}/{id}",
new RouteValueDictionary(new { controller = "Event", action = "Index", id = UrlParameter.Optional }),
new RouteValueDictionary(new { id = #"\d*" }),
new MvcRouteHandler()
)
{
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);
if (routeData == null)
{
return null;
}
var yearName = (string)routeData.Values["year-name"];
if (string.IsNullOrWhiteSpace(yearName))
{
return null;
}
var parts = yearName.Split(new[] { '-' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 2)
{
return null;
}
var year = parts.First();
int yearValue;
if (!int.TryParse(year, out yearValue))
{
return null;
}
var name = parts.Last();
routeData.Values.Add("year", year);
routeData.Values.Add("name", name);
return routeData;
}
}
and then register this route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*vti_inf}", new { vti_inf = #"(.*/)?_vti_inf.html(/.*)?" });
routes.IgnoreRoute("{*vti_rpc}", new { vti_rpc = #"(.*/)?_vti_rpc(/.*)?" });
routes.Add("NamedHomeEvent", new MyRoute());
}

Related

Unable to get webApi result in controller

I am trying to create constraint for route.Based on the access info retrieved from Db,i will choose which route to choose.
I have created controller inheriting IRouteConstraint and calling repository,but Unable to get result from httpclient call.
Code snippet of my routeConfig
routes.MapRoute(
name: "Home",
url: "{*routelink}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { routelink = new UserAccessController("Home") }
);
routes.MapRoute(
name: "Reports",
url: "{*routelink}",
defaults: new { controller = "Reports", action = "Index" },
constraints: new { routelink = new UserAccessController("Reports") }
);
Code snippet of Constarint
public class UserAccessController : IRouteConstraint
{
// GET: UserAccess
private readonly string _controller;
public UserAccessController(string Controller)
{
_controller = Controller;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
bool _authorized = false;
var userAccessDetails = GetUserRoleDetails();
switch (_controller)
{
case "Home":
{
_authorized = Convert.ToBoolean(userAccessDetails.CanAccessHome);
return _authorized;
}
case "Reports":
{
_authorized = Convert.ToBoolean(userAccessDetails.Result.CanAccessReports);
return _authorized;
}
}
return _authorized;
}
public async Task<UserRole> GetUserRoleDetails()
{
IRepository _repository = new Repository();
var userRoleDetails = new UserRole();
var currentUser = await _repository.GetCurrentUser(Path.GetFileName(HttpContext.Current.User.Identity.Name.ToUpper()));
if (currentUser != null)
{
var roles = await _repository.GetUserRoles();
userRoleDetails = roles.Where(r => r.RoleId == currentUser.RoleId).FirstOrDefault();
}
return userRoleDetails;
}
}
the repository is calling httpwrapper class to get result from httpclient.
public static async Task<IEnumerable<T>> GetAsync(IEnumerable<T> t, string path)
{
HttpResponseMessage response;
response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
t = await response.Content.ReadAsAsync<IEnumerable<T>>();
}
return t;
}
Not sure whats the issue,not getting result response = await client.GetAsync(path);.
I am able to get result with the same api and parameters when called from Session_Start event in Global.asax. Please let me know whats the issue and how can retrieve result from http.
I have resolved this issue by keeping the route config as it is.
Created a new Action filter and reroute the url based on access.
Placed this filter on the top of default route.
public class StartupFilter:ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userAccessDetails = HttpContext.Current.Session["Role"] as UserRole;
if (userAccessDetails.HasAccessHome)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(
new
{
controller = "Home",
action = "Index"
}));
}
else if (userAccessDetails.HasAccessReport))
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(
new
{
controller = "Reports",
action = "Index"
}));
}
base.OnActionExecuting(filterContext);
}
}

ASP.NET MVC Multiple actions were found that match the request

I'm trying to support both web api and normal controllers.
I have defined different methods for different actions but looks like something is wrong as I'm getting following error again and again
{"Message":"An error has occurred.","ExceptionMessage":"Multiple actions were found that match the request:
\r\nPostFilter on type imaserver.Controllers.OrdersController\r\nPostOrder on type imaserver.Controllers.OrdersController",
"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.<SendAsync>d__1.MoveNext()"}
Here is my code:
RouteConfig
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
"CDN",
"cdn/{directory}/{file}",
new { controller = "CDN", action = "Download", directory = UrlParameter.Optional, file = UrlParameter.Optional }
);
routes.MapRoute(
name: "API",
url: "api/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
}
}
OrderController
public class OrdersController : ApiController
{
private FoodDeliveryEntities db = new FoodDeliveryEntities();
// GET: api/Orders
public IQueryable<Order> GetOrders()
{
db.Configuration.ProxyCreationEnabled = false;
return db.Orders;
}
// GET: api/Orders/5
[ResponseType(typeof(Order))]
public IHttpActionResult GetOrder(long id)
{
db.Configuration.ProxyCreationEnabled = false;
Order order = db.Orders.Find(id);
if (order == null)
{
return NotFound();
}
return Ok(order);
}
// PUT: api/Orders/5
[ResponseType(typeof(void))]
public IHttpActionResult PutOrder(long id, Order order)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != order.Id)
{
return BadRequest();
}
db.Entry(order).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!OrderExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Orders/filter
[HttpPost]
public IHttpActionResult PostFilter(OrderFilter orderFilter)
{
db.Configuration.ProxyCreationEnabled = false;
if (orderFilter.OrderType != null)
return Ok(db.Orders.Include(o => o.OrderItems.Select(i => i.Item)).Include(c => c.Customer).OrderByDescending(o => o.PickupDate).Where(o => o.OrderType.Equals(orderFilter.OrderType)));
else if (orderFilter.Status != null)
return Ok(db.Orders.Include(o => o.OrderItems.Select(i => i.Item)).Include(c => c.Customer).OrderByDescending(o => o.PickupDate).Where(o => o.Status.Equals(orderFilter.Status)));
else
return Ok(db.Orders.Include(o => o.OrderItems.Select(i => i.Item)).Include(c => c.Customer));
}
// POST: api/Orders
[ResponseType(typeof(Order))]
public IHttpActionResult PostOrder(Order order)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Configuration.ProxyCreationEnabled = false;
order.Status = "New";
order.CreatedAt = DateTime.Now;
order.UpdatedAt = DateTime.Now;
db.Orders.Add(order);
db.SaveChanges();
foreach (var orderItem in order.OrderItems)
{
orderItem.OrderId = order.Id;
orderItem.CreatedAt = DateTime.Now;
orderItem.UpdatedAt = DateTime.Now;
orderItem.IsActive = true;
}
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = order.Id }, order);
}
// DELETE: api/Orders/5
[ResponseType(typeof(Order))]
public IHttpActionResult DeleteOrder(long id)
{
Order order = db.Orders.Find(id);
if (order == null)
{
return NotFound();
}
db.Orders.Remove(order);
db.SaveChanges();
return Ok(order);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool OrderExists(long id)
{
return db.Orders.Count(e => e.Id == id) > 0;
}
public partial class OrderFilter
{
public string Status { get; set; }
public string OrderType { get; set; }
}
}
I also tried to rename PostFilter to Filter but of no use
I am firing this URL:
http://somewebsite/api/Orders/filter
POST
What am I doing wrong?
Since you inherit from ApiController, you can only have 1 POST, GET, PUT or DELETE actions, to have more, update each of your actions to assign its unique route by decorating the methods with the
[ActionName("put_here_your_action_rout")]
attribute.
// POST: api/Orders/filter
[HttpPost]
[ActionName("filter")]
public IHttpActionResult PostFilter(OrderFilter orderFilter)
{
db.Configuration.ProxyCreationEnabled = false;
if (orderFilter.OrderType != null)
return Ok(db.Orders.Include(o => o.OrderItems.Select(i => i.Item)).Include(c => c.Customer).OrderByDescending(o => o.PickupDate).Where(o => o.OrderType.Equals(orderFilter.OrderType)));
else if (orderFilter.Status != null)
return Ok(db.Orders.Include(o => o.OrderItems.Select(i => i.Item)).Include(c => c.Customer).OrderByDescending(o => o.PickupDate).Where(o => o.Status.Equals(orderFilter.Status)));
else
return Ok(db.Orders.Include(o => o.OrderItems.Select(i => i.Item)).Include(c => c.Customer));
}
For the ApiController, the real name of your methods is irrelevant, you need to use the ActionName attribute.

In ASP.NET MVC, the ProcessRequest in my own MyHttpHanler just cannot be fired

I am trying to add some Routing rule in Global.ascx im my ASP.NET MVC application. I extend the IRouteHanlder, MVCHandler and registe the rule just as follow.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapRoute(
// "DefaultTT", // Route name
// "test", // URL with parameters
// new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
//);
RouteValueDictionary defaults = new RouteValueDictionary();
RouteValueDictionary constraints = new RouteValueDictionary();
RouteValueDictionary tokens = new RouteValueDictionary();
defaults.Add("controller", "home");
defaults.Add("action", "index");
defaults.Add("data", string.Empty);
constraints.Add("data", #"[a-zA-Z0-9\-]*");
tokens.Add("pageId", 0);
routes.Add(new Route("", defaults, constraints, tokens, new MyRouteHandler()));
}
public class MyRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
return new MyMVCHanlder(requestContext);
}
}
public class MyMVCHanlder : MvcHandler
{
public MyMVCHanlder(RequestContext requestContext)
: base(requestContext)
{
}
protected override void ProcessRequest(HttpContext httpContext)
{
int i = 1;
int j = i + 1;
base.ProcessRequest(httpContext);
}
protected override void ProcessRequest(HttpContextBase httpContext)
{
int i = 1;
int j = i + 1;
base.ProcessRequest(httpContext);
}
}
But when I visit localhost:50112/ in my browser, the ProcessRequest method cannot be fired. I don't know why. Do you have any thought? Thank you.

How can I split my controller name for url routing?

I want to split my controller name..
For Example;
My controller name is For_ExpController.cs
and
I want my url like this;
http://localhost/For/Exp/Action
How can I define it on my RouteConfig.cs??
You could write a custom route:
public class MyRoute: Route
{
public MyRoute()
: base(
"{part1}_{part2}/{action}",
new RouteValueDictionary(new { controller = "for_exp", action = "index" }),
new RouteValueDictionary(new { part1 = #"[a-z]+", part2 = #"[a-z]+" }),
new MvcRouteHandler()
)
{
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var rd = base.GetRouteData(httpContext);
if (rd == null)
{
return null;
}
var part1 = rd.GetRequiredString("part1");
var part2 = rd.GetRequiredString("part2");
rd.Values["controller"] = string.Concat(part1, "_", part2);
return rd;
}
}
which will be registered in your Application_Start:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("MyRoute", new MyRoute());
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
}

Map multiple indexed routes

I have a number of controllers in application:
ApiV1Controller
ApiV2Controller
ApiV3Controller
...
Is it possible to map routes for them with single MapRoute statement to URLs like /api/v1/{action}?
You could write a custom route. Let's suppose that you have the following controllers:
public class ApiV1Controller : Controller
{
public ActionResult Index()
{
return Content("v1");
}
}
public class ApiV2Controller : Controller
{
public ActionResult Index()
{
return Content("v2");
}
}
public class ApiV3Controller : Controller
{
public ActionResult Index()
{
return Content("v3");
}
}
Now write a custom route:
public class ApiRoute : Route
{
public ApiRoute()
: base("api/{version}/{action}", new RouteValueDictionary(new { action = "index" }), new MvcRouteHandler())
{
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var rd = base.GetRouteData(httpContext);
if (rd == null)
{
return null;
}
rd.Values["controller"] = "Api" + rd.GetRequiredString("version");
return rd;
}
}
That could be registered in your Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("ApiRoute", new ApiRoute());
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
and that's pretty much it. Now you could play with urls:
/api/v1
/api/v2
/api/v3

Resources