Reputation: 4007
I'm working on a new MVC 5 project. It's a single multi tenanted site that allows many organisations and branches to maintain a page. All pages start with the following url format:
http://mysite.com/{organisation}/{branch}/...
For example:
http://mysite.com/contours/albany/...
http://mysite.com/contours/birkenhead/...
http://mysite.com/lifestyle/auckland/...
I've declared my RouteConfig with the {organisation}
and {branch}
before the {controller}
and {action}
:
routes.MapRoute(
name: "Default",
url: "{organisation}/{branch}/{controller}/{action}/{id}",
defaults: new { controller = "TimeTable",
action = "Index",
id = UrlParameter.Optional });
This is working just fine. However EVERY single controller now has identical code in the top of it that examines the organisation
and branch
.
public ActionResult Index(string organisation, string branch, string name, int id)
{
// ensure the organisation and branch are valid
var branchInst = _branchRepository.GetBranchByUrlPath(organisation, branch);
if (branchInst == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// start the real code here...
}
I'm keen on the DRY principle (Don't Repeat Yourself) and I'm wondering if it's possible to somehow isolate that common code and change my controller signatures to something like this:
public ActionResult Index(Branch branch, string name, int id)
{
// start the real code here...
}
Upvotes: 3
Views: 1972
Reputation: 2810
I had to do something similar in an application where a userid and token were passed in via POST to every controller action (because of some very old legacy authentication we were doing).
I declared a BaseController and had each controller inherit that base controller. So each controller that inherits the base already has access to standard uid and token properties (if they need them), and those properties are already retrieved from the HTTP Context at the start of each action.
public abstract class BaseController : Controller
{
#region Properties
protected string uid;
protected string token;
#endregion
#region Event overloads
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
try
{
uid = requestContext.HttpContext.Request["uid"];
token = requestContext.HttpContext.Request["token"];
if (uid != null && token != null)
{
ViewBag.uid = uid;
if (!token.Contains("%"))
ViewBag.token = requestContext.HttpContext.Server.UrlEncode(token);
else
ViewBag.token = token;
base.Initialize(requestContext);
}
else
{
requestContext.HttpContext.Response.Redirect(ConfigurationManager.AppSettings.GetValues("rsLocation")[0]);
}
}
// User ID is not in the query string
catch (Exception ex)
{
requestContext.HttpContext.Response.Redirect(ConfigurationManager.AppSettings.GetValues("redirectLocation")[0]);
}
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
#endregion
}
Now you can inherit your base controller in your "real" controllers.
public class TestController : BaseController
{
#region Controller actions
//
// GET: /Index/
[Authorize]
public ViewResult Index()
{
//blah blah blah
}
}
In your case, you'd be looking for organization and branch instead of uid and token, but it's the same idea.
Upvotes: 1
Reputation: 1022
Another alternative is to use custom ActionFilters, that way you don't have to remember to inherit your base controller. The AuthorizeAttribute is a good one to overload as that's what you want to do.
public class ValidationActionFilter : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var routeData = filterContext.RouteData;
var branchInst = _branchRepository.GetBranchByUrlPath(routeData.Values["organisation"], routeData.Values["branch"]);
if (branchInst == null)
{
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
}
In your Global.asax or FilterConfig class you can then simply register it
GlobalFilters.Filters.Add(new ValidationActionFilter());
Not tested code but you get the idea..
Upvotes: 0
Reputation: 1733
You can create a common controller and have your other controllers inherit from it. For example:
public class YourCommonController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var organisation = filterContext.RouteData.Values["organisation"];
var branch = filterContext.RouteData.Values["branch"];
// Your validation code here
base.OnActionExecuting(filterContext);
}
}
Now, just inherit from YourCommonController
when you want to have this shared code.
Upvotes: 3