Наше приложение ASP.NET MVC позволяет аутентифицированному пользователю управлять одним или несколькими "сайтами", связанными с его учетной записью.
Наши URL являются весьма предположительными, так как мы используем удобное для сайта имя в URL, а не Id, например:
/sites/mysite/
/sites/mysite/settings
/sites/mysite/blog/posts
/sites/mysite/pages/create
Как видите, нам нужен доступ к названию сайта по ряду маршрутов.
Нам нужно выполнить одинаковое поведение для всех этих действий:
- Поиск сайта с указанным идентификатором на текущей учетной записи
- Если возвращаемый сайт имеет значение null, вернуть 404 (или пользовательское представление)
- Если сайт НЕ является нулевым (действительным), мы можем продолжить выполнение действия
Текущая учетная запись всегда доступна для нас через объект ISiteContext. Вот как я могу добиться всего вышеперечисленного, используя обычный параметр маршрута и выполняя запрос непосредственно в моем действии:
private readonly ISiteContext siteContext;
private readonly IRepository<Site> siteRepository;
public SitesController(ISiteContext siteContext, IRepository<Site> siteRepository)
{
this.siteContext = siteContext;
this.siteRepository = siteRepository;
}
[HttpGet]
public ActionResult Details(string id)
{
var site =
siteRepository.Get(
s => s.Account == siteContext.Account && s.SystemName == id
);
if (site == null)
return HttpNotFound();
return Content("Viewing details for site " + site.Name);
}
Это не так уж плохо, но мне нужно будет сделать это примерно на 20 методах действий, поэтому я хочу, чтобы все было сухо, насколько это возможно.
Я не так много сделал с пользовательскими моделями, так что мне интересно, подходит ли эта работа для них лучше? Ключевое требование заключается в том, что я могу внедрить свои зависимости в связыватель модели (для ISiteContext и IRepository - при необходимости я могу прибегнуть к DependencyResolver).
Большое спасибо,
Ben
Обновление
Ниже приведен рабочий код с использованием как пользовательского связывателя модели, так и фильтра действий. Я до сих пор не уверен, что я чувствую по этому поводу, потому что
- Должен ли я попасть в свою базу данных из связывателя моделей
- I может на самом деле выполнять как извлечение объекта, так и проверку нуля из фильтра действий. Что лучше?
Модель Binder:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (!controllerContext.RouteData.Values.ContainsKey("siteid"))
return null;
var siteId = controllerContext.RouteData.GetRequiredString("siteid");
var site =
siteRepository.Get(
s => s.Account == siteContext.Account && s.SystemName == siteId
);
return site;
}
Фильтр действий:
public class ValidateSiteAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var site = filterContext.ActionParameters["site"];
if (site == null || site.GetType() != typeof(Site))
filterContext.Result = new HttpNotFoundResult();
base.OnActionExecuting(filterContext);
}
}
Действия контроллера:
[HttpGet]
[ValidateSite]
public ActionResult Settings(Site site)
{
var blog = site.GetFeature<BlogFeature>();
var settings = settingsProvider.GetSettings<BlogSettings>(blog.Id);
return View(settings);
}
[HttpPost]
[ValidateSite]
[UnitOfWork]
public ActionResult Settings(Site site, BlogSettings settings)
{
if (ModelState.IsValid)
{
var blog = site.GetFeature<BlogFeature>();
settingsProvider.SaveSettings(settings, blog.Id);
return RedirectToAction("Settings");
}
return View(settings);
}