如何重载MVC控制器以避免重复公共代码?

时间:2013-10-03 01:28:04

标签: asp.net-mvc overloading asp.net-mvc-5

我正在开发一个新的MVC 5项目。它是一个单一的多租户站点,允许许多组织和分支机构维护页面。所有页面都以以下网址格式开头:

http://mysite.com/{organisation}/{branch}/...

例如:

http://mysite.com/contours/albany/...   
http://mysite.com/contours/birkenhead/...   
http://mysite.com/lifestyle/auckland/...   

我已在{organisation}{branch}之前使用{controller}{action}声明了我的RouteConfig:

routes.MapRoute(
    name: "Default",
    url: "{organisation}/{branch}/{controller}/{action}/{id}",
    defaults: new { controller = "TimeTable", 
                    action = "Index", 
                    id = UrlParameter.Optional });

这很好用。但是,每个单个控制器现在都在其顶部具有相同的代码,用于检查organisationbranch

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...
}

我热衷于DRY原则(不要重复自己),我想知道是否有可能以某种方式隔离该公共代码并将我的控制器签名更改为:

public ActionResult Index(Branch branch, string name, int id)
{
    // start the real code here...
}

3 个答案:

答案 0 :(得分:3)

您可以创建一个通用控制器,让其他控制器继承它。例如:

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);
        }
    }

现在,只要您希望拥有此共享代码,就可以继承YourCommonController

答案 1 :(得分:1)

我必须在应用程序中执行类似的操作,其中userid和token通过POST传递给每个控制器操作(因为我们正在进行一些非常古老的遗留身份验证)。

我声明了一个BaseController并让每个控制器继承该基本控制器。因此,每个继承基础的控制器都可以访问标准的uid和令牌属性(如果需要),并且在每个操作开始时已经从HTTP上下文中检索了这些属性。

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
    }

现在,您可以在“真实”控制器中继承基本控制器。

public class TestController : BaseController
    {
        #region Controller actions

        //
        // GET: /Index/     

        [Authorize]
        public ViewResult Index()
        {
             //blah blah blah
        }
     }

在您的情况下,您将寻找组织和分支而不是uid和令牌,但这是相同的想法。

答案 2 :(得分:0)

另一种方法是使用自定义ActionFilters,这样您就不必记住继承基本控制器。 AuthorizeAttribute是一个很好的重载,因为这就是你想要做的事情。

http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs

 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);
           }   
      }
 }

在Global.asax或FilterConfig类中,您只需注册

即可
GlobalFilters.Filters.Add(new ValidationActionFilter());

没有经过测试的代码,但你明白了......