Sometimes you want to take control over all of your requests prior to them getting too deep into your application. While you could do this with an HttpHandler
or an HttpModule
, that wouldn't be very MVC-like. Instead, we will implement this sort of logic in the next step of the pipeline by way of a custom RouteConstraint
.
In our example, we will take a look at implementing a blacklist filter, using a wide open wildcard route and a RouteConstraint
that checks all requests against a few different types of blacklists. If the user is on the blacklist, we will have the option to route them off our site or to a page in our site specifically for blacklisted folks.
Global.asax
file. In the routing section, at the top of the list of routes, we need to create a new route. This route will be named BlacklistFilter
and will have only a wildcard entry for its path pattern matching, essentially funneling all requests through this route for a check. Then comes the magic in that we will specify a custom RouteHandler
called BlacklistConstraint
.Global.asax:
routes.MapRoute("BlacklistFilter", "{*path}", new { controller = "Blacklist", action = "Index", }, new {isBlacklisted = new BlacklistConstraint()} );
BlacklistController
in the Controllers
folder. This controller will have one action on it named Index
and will take in a parameter named reason which we will use to capture the reason the user is on the blacklist.Controllers/BlacklistController.cs:
public class BlacklistController : Controller { public ActionResult Index(string reason) { ViewData["Reason"] = reason; return View(); } }
Index
view, which will be an empty view. In the view, we will display a simple message to the user telling them that they have been blacklisted. We will also say why we think they have been blacklisted.Views/Blacklist/Index.aspx:
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Blacklisted! </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>Uh oh!</h2> <div> It looks like you have been blacklisted. If you feel that you are seeing this mistakenly please contact us at... </div> <fieldset> <legend>Why was I blacklisted?</legend> <%= ViewData["reason"] %> </fieldset> </asp:Content>
RouteConstraint
. Create a new class in the Models
folder named RouteConstraint
. Set this new class to implement IRouteConstraint
and then create the one required method, Match()
.Models/BlacklistConstraint.cs:
public class BlacklistConstraint : IRouteConstraint { public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { } }
Boolean
result. Whatever method you use to determine if a user is on your blacklist is up to you. If you locate the user on the blacklist, return true, which will route them to your blacklisted page. If you don't find them, return false, and the processing will continue. You may also want to drop a cookie or set a session variable so that the check is not repeated over and over—unless you want to. For our recipe, we will define some blacklists directly in the class, for example purposes only. We will also create a simple variable to track if this user is flagged or not.Models/BlacklistConstraint.cs:
bool blacklisted = false; //you could get your blacklist data from a database //and cache it when the app starts //ip address list List<string> _ipBlacklist = new List<string>() { "127.0.0.1", "192.168.1.100", "192.168.1.101", "192.168.1.102", "192.168.1.103", "192.168.1.104", "192.168.1.105", "192.168.1.106", "192.168.1.107", "192.168.1.108", "192.168.1.109", "192.168.1.110" }; //username list List<string> _usernameBlacklist = new List<string>() { "asiemer", "anonymous" }; //email list List<string> _emailBlacklist = new List<string>() { "[email protected]", "[email protected]" };
reason
variable in our route data that can be displayed in our blacklist index view.Models/BlacklistConstraint.cs:
//check ip addresses if (_ipBlacklist.Contains(httpContext.Request.UserHostAddress)) { values.Add("Reason", "You were blacklisted because of your IP address: " + httpContext.Request.UserHostAddress); blacklisted = true; } //check usernames if (httpContext.Profile != null && _usernameBlacklist.Contains( httpContext.Profile.UserName.ToLower())) { values.Add("Reason", "You were blacklisted because of your username: " + httpContext.Profile.UserName.ToLower()); blacklisted = true; } //check email addresses if (_emailBlacklist.Contains("values['email'].ToString()")) { values.Add("Reason", "You were blacklisted because of your email address: values['email'].ToString()"); blacklisted = true; }
HttpStatusCode
of our response to access denied (403).Be aware that telling your blacklisted user that they are actually blacklisted, as well as why they are blacklisted, may not be the best way to handle such a user. If it is to be a temporary blacklist due to abuse of a site feature, telling the user might be appropriate. Telling a malicious user that they are blacklisted due to their IP address might get them to go through a proxy server prior to coming to your site. Just think about what you do here.
Models/BlacklistConstraint.cs:
//do some stuff before booting the user if (blacklisted) { //of, if you don't want to keep them around... //httpContext.Response .Redirect("http://www.peopleofwalmart.com"); //set the status code to access denied httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } //return the result return blacklisted;
127.0.0.1
. If for some reason you are not blacklisted by IP address, you can run ipconfig
at the command prompt to get your current IP. Or you can create an account in the site and add your username to the username list.This recipe takes advantage of an extension point of the MVC framework. Specifically, we used a custom RouteConstraint
to add to our route definition. The nice bit about the RouteConstraint
is that anything goes here, as it is just a standard class from which we can do pretty much anything. The combination of the route definition and this added bit of Boolean logic makes your routing capabilities very powerful.