I build a site in mvc3 , i want to restrict my site on firefox .
i mean to say that when anyone open my site on firefox it open correctly but when anyone opens it on chrome or IE it give an customze error . I am using c# with mvc3
You could write a global action filter which will test the User-Agent HTTP request header:
public class FireFoxOnlyAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var userAgent = filterContext.HttpContext.Request.Headers["User-Agent"];
if (!IsFirefox(userAgent))
{
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/Unauthorized.cshtml"
};
}
}
private bool IsFirefox(string userAgent)
{
// up to you to implement this method. You could use
// regular expressions or simple IndexOf method or whatever you like
throw new NotImplementedException();
}
}
and then register this filter in Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new FireFoxOnlyAttribute());
}
You are looking for the user-agent of the user connected to your website, which may be retrieved via this call in your controller:
Request.UserAgent
Not that I agree with such a pattern, though.
This is a simple javascript function you may add to your code and perform the actions against.
function detect_browser() {
var agt=navigator.userAgent.toLowerCase();
if (agt.indexOf("firefox") != -1) return true;
else{
window.location="";//Here within quotes write the location of your error page.
}
}
On main page you may call the function on page load event. Though this practice is not recommended.
You could test the Request.UserAgent as part of a constraint on the route.
For example, you could define a route constraint routine as follows:
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);
}
}
Then add the following constraint to a route:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, //Parameter defaults
new { customConstraint = new UserAgentConstraint("Firefox") } //Constraint
);
Related
I have a custom MvcRouteHandler which checks database if a Url exists and pairs it with some controller action and Id.
However if this route handler can not find a matching pair in database, I'd like MVC to keep try with other defined route handlers in route table.
How can I do that?
Update: (Example code added)
routes.MapRoute(
name: "FriendlyRoute",
url: "{FriendlyUrl}").RouteHandler = new FriendlyRouteHandler();
FriendlyRouteHandler is:
public class FriendlyRouteHandler : MvcRouteHandler
{
private TancanDbContext db = new MyDbContext();
protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
if (requestContext.RouteData.Values["FriendlyUrl"] != null)
{
string friendlyUrl = requestContext.RouteData.Values["FriendlyUrl"].ToString();
//Here, you would look up the URL Record in your database, then assign the values to Route Data
//using "where urlRecord.Url == friendlyUrl"
try
{
UrlRecord urlRecord = db.UrlRecords.Single(u => u.URL == friendlyUrl);
//Now, we can assign the values to routeData
if (urlRecord != null)
{
requestContext.RouteData.Values["controller"] = urlRecord.Controller;
requestContext.RouteData.Values["action"] = urlRecord.Action;
if(urlRecord.EntityId != null)
requestContext.RouteData.Values["id"] = urlRecord.ObjectId;
}
}
else
{
//Here, I want to redirect to next RouteHandler in route Table
requestContext.RouteData.Values["controller"] = friendlyUrl;
}
}
catch (Exception ex)
{
//throw;
//Here too, I want to redirect to next RouteHandler in route Table
requestContext.RouteData.Values["controller"] = friendlyUrl;
}
}
return base.GetHttpHandler(requestContext);
}
}
After adding this line it seems to work:
requestContext.RouteData.Values["controller"] = friendlyUrl;
Am I lucky or this is right way to do ? Do I need to use IRouteConstraint somewhere?
By the way, my influence was this article by Adam Riddick.
You want to use a custom Constraint, not a custom Handler for this.
routes.MapRoute(
name: "example",
url: "{friendly}",
defaults: new { controller = "FriendlyController", action = "Display" },
constraints: new { friendly = new FriendlyUrlConstraint() }
);
and then the constraint becomes:
public class FriendlyUrlConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var friendlyUrl = values[parameterName];
// Your logic to return true/false here
}
}
How to properly apply global action filter so it triggers on all actions, and then returns Custom result of action that triggered the filter? I will provid example of what i have done so far, but been unable to trigger the filterContext.Result = ...
Global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new RequestCounter());
}
public class RequestCounter : ActionFilterAttribute
{
public override void OnResultExecuted(ActionExecutingContext filterContext)
{
if (!LogCounter())
{
if (!filterContext.IsChildAction)
{
var values = new RouteValueDictionary(new
{
controller = "Account",
action = "LogOff"
});
filterContext.Result = new RedirectToRouteResult(values);
//--> Here, the action is not redirected to LogOff method,
// the goal is to logoff user, program just continues???
}
}
base.OnResultExecuted(filterContext);
}
}
How to properly redirect to LogOff() method, from this context, using Result or some other way as well?? thanks!
According to this answer on a similar question, you need to assign the Area parameter to an empty string e.g.
var values = new RouteValueDictionary(new
{
controller = "Account",
action = "LogOff",
area = ""
});
I want to be able to handle any url that s requested via some controller.
foo.com/a
foo.com/abcd
foo.com/x1
for foo.com/a
I want to process it with
UrlHandlerController with Process(string url) method.
How should i add a routing rule to be able to do this?
Any ideas?
Create a new custom route and use Phill Haack's Route Debugger to test your routes:
routes.MapRoute(
"customroute",
"{url}",
new { controller = "UrlHandler",
action = "Process",
url = ""
}
);
Controller:
public class UrlHandlerController : Controller
{
[HttpGet]
public ActionResult Process(string url)
{
return View();
/* or */
if(url == "something"){
return View("SomethingView");
}
else if(url == "somethingelse"){
return View("SomethingElseView");
}
}
}
Darth, see if this route helps:
routes.MapRoute(
"CustomRoute", // Route name
"{url}", //Route formation
new { controller = "UrlHandler", action = "Process" }, // Where to send it
new { keyWord = #"\S+" } // Regex to identify the argument
);
Regards.
So I have a method that accepts some JSON data and binds the data to some variables. The route only has the method name in it, nothing else.
Is it possible to have a route constraint that looks at the POST data and checks some variables to determine whether it is the correct route?
The methods:
public ActionResult GetDataV1(string userId)
{
// Do stuff one way
}
public ActionResult GetDataV2(string userId)
{
// Do stuff another way
}
The routes:
routes.MapRoute(
"API GetData V1",
"GetData",
new { controller = "API", action = "GetDataV1" },
new { version = new APIVersionRoutingConstraint("1") }
);
routes.MapRoute(
"API GetData V2",
"GetData",
new { controller = "API", action = "GetDataV2" },
new { version = new APIVersionRoutingConstraint("2") }
);
The client would post { "version":"1", "userId":"mrjohn" } to /GetData and would get response from GetDataV1. The APIVersionRoutingConstraint would make sure the right method is called depending on the version variable.
Would it be good practice to try to deserialise the request stream inside the constraint? Maybe it would be better to put the version in the URL like /GetData/1 and the other variables in the JSON body?
Rather than trying to check the version as a route constraint, why not go to one Action that then checks the version to execute the appropriate work?
I haven't tested this, but the idea is:
public ActionResult GetData(string userId, int version)
{
switch(version)
{
case 1:
return GetDataV1(userId);
case 2:
return GetDataV2(userId);
// You could always add more cases here as you get more versions,
// and still only have the one route
default:
// Probably more appropriate to return a result that contains error info,
// but you get the idea
throw new ArgumentOutOfRangeException("version");
}
}
// Made these private since they are called from the public action
private ActionResult GetDataV1(string userId)
{
// Do stuff one way
}
private ActionResult GetDataV2(string userId)
{
// Do stuff another way
}
And then you only need the one route
routes.MapRoute(
"GetData",
"GetData",
new { controller = "API", action = "GetData" },
new { version = "\d+" } // Require a numeric version
);
Made the APIVersionRoutingConstraint like this:
public class APIVersionRoutingConstraint : IRouteConstraint
{
private string _versionNumber;
public APIVersionRoutingConstraint(string versionNumber)
{
_versionNumber = versionNumber;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string version = null;
if (httpContext.Request.ContentType.Contains("application/json"))
{
string body = new StreamReader(httpContext.Request.InputStream).ReadToEnd();
httpContext.Request.InputStream.Position = 0;
var vals = new JavaScriptSerializer().DeserializeObject(body) as Dictionary<string, object>;
if (vals.ContainsKey("version"))
version = vals["version"].ToString();
}
// Must have version to pass constraint
return !string.IsNullOrEmpty(version) && version == _versionNumber;
}
}
It's not super-efficient I guess, but it gets the job done. Only the route that matches the version in the POST body gets used.
This is my goal:
I need to two (or more) "Areas" for my MVC web app. They would be accessed like so:
/* Home */
http://example.com/
http://example.com/about
http://example.com/faq
http://example.com/contact
/* Admin */
http://example.com/admin
http://example.com/admin/login
http://example.com/admin/account
http://example.com/admin/ect
I would like to organize the project like the following:
MyExampleMVC2AreasProject/
Areas/
Admin/
Controllers/
Models/
Views/
Home/
Shared/
Site.Master
Web.Config
AdminAreaRegistration.cs
Web/
Controllers/
Models/
Views/
Home/
Shared/
Site.Master
Web.Config
WebAreaRegistration.cs
Global.asax
Web.Config
So, in Global.asax I have:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
Here is WebAreaRegistration.cs
using System.Web.Mvc;
namespace MyExampleMVC2AreasProject.Areas.Web
{
public class WebAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Web";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"WebDefault",
"{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
'AdminAreadRegistration.cs' is set up the same but the url param is Admin/{action}/{id}.
With the setup above the Web "Area" works great (example.com/about, example.com/contact, etc).
What do I need to do to get the Admin "Area" hooked up with the routes the way I want them? I just get 404ed now.
I've tried every combination of routes, routes w/namespaces, URL Parameters, parameter defaults, etc, I could think of. I have a feeling I'm missing something pretty basic.
I use this AreaRegistrationUtil class. It automatically registers anything which inherits AreaRegistration in any assembly you specify. As an added bonus, it's WAY faster than AreaRegistration.RegisterAllAreas because it only looks at the assembly you specify.
You probably need to set your namespaces on all your area registrations.
Example
context.MapRoute(
"Admin_default",
"admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "MyExampleMVC2AreasProject.Areas.Admin.Controllers" } // This is area namespace
);
My current solution is here: http://notesforit.blogspot.com/2010/08/default-area-mvc-2.html
I don't like it and would love to get a better solution.
-- copied from URL above:
Implement your Areas as usually, register any routes that you need.
For example:
public class PublicAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Public";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Public_default",
"Public/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional, }
);
}
}
And:
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new {controller = "Overview", action = "Index", id = UrlParameter.Optional }
);
}
}
It's important, that URL should have any prefix, for example http://site.com/PREFIX/{controller}/{action}, because prefix of default Area will be cropped
Next in Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
public static string _defaultAreaKey = "DefaultArea";
public static void RegisterDefaultRoute(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//reading parameter DefaultArea from web.config
string defaultArea = ConfigurationManager.AppSettings[_defaultAreaKey];
//check for null
if (String.IsNullOrEmpty(defaultArea))
throw new Exception("Default area isn\'t set");
//select routes from registered,
//which have DataTokens["area"] == DefaultArea
//Note, each Area might have more than one route
var defRoutes = from Route route in routes
where
route.DataTokens != null &&
route.DataTokens["area"] != null &&
route.DataTokens["area"].ToString() == defaultArea
select route;
//cast to array, for LINQ query will be done,
//because we will change collection in cycle
foreach (var route in defRoutes.ToArray())
{
//Important! remove from routes' table
routes.Remove(route);
//crop url to first slash, ("Public/", "Admin/" etc.)
route.Url = route.Url.Substring(route.Url.IndexOf("/") + 1);
//Important! add to the End of the routes' table
routes.Add(route);
}
}
protected void Application_Start()
{
//register all routes
AreaRegistration.RegisterAllAreas();
//register default route and move it to end of table
RegisterDefaultRoute(RouteTable.Routes);
}
}
Do not forget add parameter to web.config:
<configuration>
<appSettings>
<add key="DefaultArea" value="Public"/>
</appSettings>
<!-- ALL OTHER-->
</configuration>