2013-10-03 3 views
3

Я работаю над новым проектом MVC 5. Это один многоэтажный сайт, который позволяет многим организациям и филиалам поддерживать страницу. Все страницы Начнут со следующим форматом URL:Как вы перегружаете MVC-контроллеры, чтобы избежать повторения общего кода?

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

Например:

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

Я объявил свою RouteConfig с {organisation} и {branch} перед {controller} и {action}:

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

Это работает отлично. Однако КАЖДЫЙ один контроллер теперь имеет идентичный код в верхней части, который анализирует organisation и 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... 
} 

Увлекаюсь СУХОЙ принципе (не повторяться), и мне интересно, если это возможно, чтобы как-то выделить, что общий код и изменить свои подписи контроллера на что-то вроде этого:

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

ответ

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, когда вы хотите, чтобы этот общий код.

+0

Хорошо, я уже пробовал что-то подобное, но даже после проверки мой 'branchInst' недоступен в контроллере, который наследует от этого. –

+0

Спасибо, это заставило меня задуматься о том, ПОЧЕМУ это работает, и я наконец понял, что объекты Controller на самом деле действительно недолговечные объекты, и мне не нужно было беспокоиться о том, что переменная уровня члена будет использоваться вне контекста. Это сделало контроллер более понятным для меня! –

0

Другой альтернативой является использование пользовательских ActionFilters, так что вам не нужно помнить о наследовании вашего базового контроллера. Авторизованный атрибут является хорошим, чтобы его можно было перегружать, поскольку это то, что вы хотите сделать.

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

Не тестировался код, но вы получите идею ..

1

я должен был сделать что-то подобное в приложении, где идентификатор пользователя и токен были переданы через POST для каждого действия контроллера (из-за очень старой устаревшей аутентификации мы были doi нг).

Я объявил BaseController и каждый контроллер наследовал этот базовый контроллер. Таким образом, каждый контроллер, который наследует базу, уже имеет доступ к стандартным свойствам uid и token (если они им нужны), и эти свойства уже извлекаются из контекста 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 и токена, но это та же идея.

Смежные вопросы