I created this Custom Route Class in ASP.NET MVC:
public class UserAgentConstraint:IRouteConstraint {
private string RequiredUserAgent;
public UserAgentConstraint(string agentParam) {
RequiredUserAgent = agentParam;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection) {
return httpContext.Request.UserAgent != null && !httpContext.Request.UserAgent.Contains(RequiredUserAgent);
}
}
And in Global.asax.cs:
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("myRoute2", "{controller}/{action}/{Id}",
new { controller = "home", action = "index", Id = UrlParameter.Optional }, new {
customConstriant=new UserAgentConstraint("IE")
}
}
The above code works prefectly, but when the user uses IE, I get a 404 Error. I want to redirect to a custom Page. I dont want to use a Custom Error in the Web.Config file because my error is only for use in IE. How can one do this?
Thanks in your advice.
a better way of doing this is using ActionFilter.
public class BrowserFilterAttribute : ActionFilterAttribute
{
public string [] _browserNames { get; set; }
public AssetIdFilterAttribute(params string [] browserNames)
{
_browserNames= browserNames;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//get browser name from somewhere
string currentBrowser = filterContext.HttpContext.Request.Browser.Browser;
if(_browserNames.Contains(currentBrowser))
filterContext.Result = new RedirectResult("your URL");
}
}
you can apply it in Controller level like this :
[BrowserFilter("IE","Opera","SomeOtherBrowser")]
public class BrowserAwareController() : Controller
{
}
hope this help.good luck.
Related
I am working on CMS and need to do dynamic routing. I tried a lot of things but I decided to use routing like below.
[Route("{leftPath:values(announcements|duyurular)}/{announceUrl?}")]
public ActionResult Announcements(string leftPath, string announceUrl)
{
ViewBag.Url = announceUrl;
return View();
}
Problem:
[Route("{leftPath:values(announcements|duyurular)}/{announceUrl?}")]
bold part is dynamic. For example, if I use only English for user interface langage, I want to 'announcements' instead of bolded part above. If I use English, Turkish and Spanish I want to 'announcements|duyurular|anuncios'.
If I do like below, I get error because Attributes are accepts constant string variables:
[Route("{leftPath:values(" + GetRouteValues() + ")}/{announceUrl?}")]
public ActionResult Announcements(string leftPath, string announceUrl)
{
ViewBag.Url = announceUrl;
return View();
}
Is there any way to do this or any suggestions.
I am sorry about this but I found my solution, I didn't try solutions enough.
This code is fixed my problem. (in RouteConfig class -> RegisterRoutes() function.):
routes.MapRoute(
name: "announcementRoute",
url: "{leftPath}/{announcementUrl}",
defaults: new { controller = "Home", action = "Announces", announcementUrl = UrlParameter.Optional },
constraints: new { leftPath = new ValuesConstraint(Tools.GetRouteValues("announcements")) }
);
constraints: new {
leftPath = new ValuesConstraint (Tools.GetRouteValues ( "announcements" ))
}
Bolded function returns string what i want: 'announcements' or 'announcements|duyurular|anuncios' based on language(s).
For the curious:
public class ValuesConstraint : IRouteConstraint
{
private readonly string[] validOptions;
public ValuesConstraint(string options)
{
validOptions = options.Split('|');
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
}
return false;
}
}
more: MSDN Resource
I have a ListItem class that is used to represent menu items in my application:
public class ListItem : Entity
{
public virtual List List { get; set; }
public virtual ListItem ParentItem { get; set; }
public virtual ICollection<ListItem> ChildItems { get; set; }
public int SortOrder { get; set; }
public string Text { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public string Area { get; set; }
public string Url { get; set; }
}
I use this data to construct the routes for the application, but I was wondering if there was a clean way to handle controller/views for static content? Basically any page that doesn't use any data but just views. Right now I have one controller called StaticContentController, which contains a unique action for each static page that returns the appropriate view like so:
public class StaticContentController : Controller
{
public ActionResult Books()
{
return View("~/Views/Books/Index.cshtml");
}
public ActionResult BookCategories()
{
return View("~/Views/Books/Categories.cshtml");
}
public ActionResult BookCategoriesSearch()
{
return View("~/Views/Books/Categories/Search.cshtml");
}
}
Is there some way I could minimize this so I don't have to have so many controllers/actions for static content? It seems like when creating my ListItem data I could set the Controller to a specific controller that handles static content, like I have done, but is there anyway to use one function to calculate what View to return? It seems like I still need separate actions otherwise I won't know what page the user was trying to get to.
The ListItem.Url contains the full URL path from the application root used in creating the route. The location of the View in the project would correspond to the URL location to keep the organization structure parallel.
Any suggestions? Thanks.
Edit: My Route registration looks like so:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("Shared/{*pathInfo}");
routes.MapRoute("Access Denied", "AccessDenied", new { controller = "Shared", action = "AccessDenied", area = "" });
List<ListItem> listItems = EntityServiceFactory.GetService<ListItemService>().GetAllListItmes();
foreach (ListItem item in listItems.Where(item => item.Text != null && item.Url != null && item.Controller != null).OrderBy(x => x.Url))
{
RouteTable.Routes.MapRoute(item.Text + listItems.FindIndex(x => x == item), item.Url.StartsWith("/") ? item.Url.Remove(0, 1) : item.Url, new { controller = item.Controller, action = item.Action ?? "index" });
}
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
You can use a single Action with one parameter (the View name) which will return all the static pages
public class StaticContentController : Controller
{
public ActionResult Page(string viewName)
{
return View(viewName);
}
}
You will also need to create a custom route for serving these views, for example:
routes.MapRoute(
"StaticContent", // Route name
"page/{viewName}", // URL with parameters
new { controller = "StaticContent", action = "Page" } // Parameter defaults
);
I see in your example that you specify different folders for your views. This solution will force you to put all static views in the Views folder of the StaticContentController.
If you must have custom folder structure, then you can change the route to accept / by adding * to the {viewName} like this {*viewname}. Now you can use this route: /page/Books/Categories. In the viewName input parameter you will receive "Books/Categories" which you can then return it as you like: return View(string.Format("~/Views/{0}.cshtml", viewName));
UPDATE (Avoiding the page/ prefix)
The idea is to have a custom constraint to check whether or not a file exists. Every file that exists for a given URL will be treated as static page.
public class StaticPageConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string viewPath = httpContext.Server.MapPath(string.Format("~/Views/{0}.cshtml", values[parameterName]));
return File.Exists(viewPath);
}
}
Update the route:
routes.MapRoute(
"StaticContent", // Route name
"{*viewName}", // URL with parameters
new { controller = "StaticContent", action = "Page" }, // Parameter defaults
new { viewName = new StaticPageConstraint() } // Custom route constraint
);
Update the action:
public ActionResult Page(string viewName)
{
return View(string.Format("~/Views/{0}.cshtml", viewName));
}
1) What is the best solution for working with T4MVC to have generated formatted URL (SEO)
I want to MVC.AGENCY.INDEX (int? Page, int IdAgency)
http://localhost:30120/Agency/AgencyName
instead
http://localhost:30120/Agency?page=0&IdAgence=2
I can have this
http://localhost:30120/Agency?page=0&IdAgency=2&agency=agencyName
with AddMaperoute() but I don't want (Agency?page=0&IdAgency=2) in the URL
maybe change the symbols & and = by /?
2) When I add
I use
http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/
<input type="submit" name=="Agency" value="" class="button bbrightRed mr25" />
public virtual ActionResult Agency (AgencyViewModel _AgencyViewModel)
{
....
View return (_AgencyViewModel). AddRouteValue ("AgencyName", AgencyName);
}
I want add some information URL
I have an exeption when i add View return (_AgencyViewModel). AddRouteValue ("AgencyName", AgencyName);
Incorrectly Called T4MVC WAS. You may Need to power to regenerate it by right clicking will be T4MVC.tt and Choosing Run Custom Tool
My URL without AddRouteValue() ishttp://localhost:30120/Agency
And I want
http://localhost:30120/Agency/Agancyname/fff-llll-mm
If you don't need page=0&IdAgency=2 you have at least 2 options:
replace it with url like http://localhost:30120/Agency/AgencyName/2/0 and using MVC.AGENCY.INDEX (string name, int? Page, int IdAgency) (see Way1 in routing below)
remove id and page from the controller at all and map only by name (only when it is unique). You'll have http://localhost:30120/Agency/AgencyName and using MVC.AGENCY.INDEX (string name) (see Way2 in routing below)
To have seo urls you need to register routes. You can do that in Application_Start method in Global.asax. Here is a good overview
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Map("Way1", "Agency/{name}/{IdAgency}/{Page}", MVC.Agency.Index().AddRouteValue("page", 1)
, new { Page = #"\d*" } );
routes.Map("Way2", "Agency/{name}", MVC.Agency.Index() );
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
Here is I have created several extensions to be used with T4MVC
public static class RouteExtensions
{
#region Map
public static Route Map(this RouteCollection routes, string routename, string url,
ActionResult result)
{
return routes.Map(routename, url, result, null, null, null);
}
public static Route Map(this RouteCollection routes, string routename, string url,
ActionResult result, object constraints)
{
return routes.Map(routename, url, result, null, constraints, null);
}
public static Route Map(this RouteCollection routes, string routename, string url,
ActionResult result, object defaults, object constraints, string[] namespaces)
{
return routes.MapRoute(routename, url, result, defaults, constraints, namespaces)
.SetRouteName(routename);
}
#endregion
public static string GetRouteName(this RouteValueDictionary routeValues)
{
if (routeValues == null)
{
return null;
}
object routeName = null;
routeValues.TryGetValue("__RouteName", out routeName);
return routeName as string;
}
public static Route SetRouteName(this Route route, string routeName)
{
if (route == null)
{
throw new ArgumentNullException("route");
}
if (route.DataTokens == null)
{
route.DataTokens = new RouteValueDictionary();
}
route.DataTokens["__RouteName"] = routeName;
return route;
}
}
I want to make the following:
when the url doesn't have an instID, i want to redirect to the "Instelling" action
in this controller, every method needs the instID.
[RequiredParameter(parameterName="instID", controllerToSend="Instelling")]
public ActionResult Index(int? instID) {
//if (!instID.HasValue) {
// return RedirectToAction("Index", "Instelling");
//}
var facts = _db.Instellingens.First(q => q.Inst_ID == instID).FacturatieGegevens;
return View(facts);
}
so this is in the controller.
the actionfilter:
namespace MVC2_NASTEST.Controllers {
public class RequiredParameterAttribute : ActionFilterAttribute {
public string parameterName { get; set; }
public string actionToSend { get; set; }
public string controllerToSend { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (parameterName != string.Empty) {
if (filterContext.ActionParameters.ContainsKey(parameterName) && filterContext.ActionParameters[parameterName] != null) {
string s = "test";
//all is good
} else {
//de parameter ontbreekt. kijk of de controller en de action geset zijn.
if (actionToSend == string.Empty)
actionToSend = "Index";
if (controllerToSend == string.Empty) {
controllerToSend = filterContext.Controller.ToString();
controllerToSend = controllerToSend.Substring(controllerToSend.LastIndexOf(".") + 1);
controllerToSend = controllerToSend.Substring(0, controllerToSend.LastIndexOf("Controller"));
}
UrlHelper helper = new UrlHelper(filterContext.RequestContext);
string url = helper.Action(actionToSend, controllerToSend);
HttpContext.Current.Response.Redirect(url);
//filterContext.HttpContext.Response.Redirect(url, true);
}
}
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext) {
base.OnActionExecuted(filterContext);
}
}
}
the thing is: it does work, however, the action itself first gets executed, THEN the redirect happens. this is not what I wanted.
Perhaps i shouldnt use actionfilters but just add a route?
in this case, how would i redirect the route to another controller if the instID is missing?
Rather than creating an action filter (which runs just before the return of the action method), you could consider changing to an Authorization Filter which would allow you to redirect to an alternative controller & action
Something like this (pseudo code):
public class RequiredParameterAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// read instID from QueryString
// if instId is null, return false, otherwise true
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.result = new RedirectToRouteResult( new { controller = "MyController" , action = "MyAction" } )
}
}
This was the first result on a question I asked on Google, so I'd like to propose a different answer. Instead of doing the redirect from the action assign a redirect to the filterContext.Result like this:
filterContext.Result = new RedirectResult(url);
If the result property of the filterContext is not null then the underlying action will not be executed. Because you're performing a redirect outside the context of the call you will still execute the action method.
Hy, in my Global.asax I've this rule:
// Home
routes.MapRoute("Home",
"{lang}/",
new { lang = "ita", controller = "Home", action = "Index" },
new { lang = new LanguageRouteConstraint() }
);
And my LanguageRouteConstraint class:
public class LanguageRouteConstraint : IRouteConstraint
{
#region Membri di IRouteConstraint
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if ((routeDirection == RouteDirection.IncomingRequest) && (parameterName.ToLower(CultureInfo.InvariantCulture) == "lang"))
{
try
{
string lang = Convert.ToString(values[parameterName]);
// Language check on db
Language currLang = new Language().Get(lang);
if (currLang != null)
{
// Here I'd like to "save (in session|querystring|....)" the id
return true;
}
}
catch
{
return false;
}
}
return false;
}
#endregion
}
And my controller
public class HomeController : Controller
{
public ActionResult Index(string lang)
{
// I would get the language ID without interrogating the data base
}
}
In HomeController-->Index method I would get the language ID without interrogating the data base because I have already done in LanguageRouteConstraint.
I'm sorry for my poor English
Thanks in advance.
You could do the following:
In the Match method insert the language ID in the RouteValueDictionary: values["lang"] = theLanguageId;
Turn your action's signature into something like ActionResult Index(int lang)
You can access the current session via httpContext on your language constraint
Language currLang = new Language().Get(lang);
if (currLang != null)
{
httpContext.Session["Lang"] = id
return true;
}
then in your controller you could use a property
public int Language { get return int.Parse(Session["Lang"].ToString()); }