【问题标题】:Route Parameter, Custom Model Binder or Action Filter?路由参数、自定义模型绑定器或操作过滤器?
【发布时间】:2011-05-24 15:59:36
【问题描述】:

我们的 ASP.NET MVC 应用程序允许经过身份验证的用户管理与其帐户相关联的一个或多个“站点”。

我们的网址高度易于猜测,因为我们在网址中使用网站友好名称而不是 Id,例如:

/sites/mysite/
/sites/mysite/settings

/sites/mysite/blog/posts
/sites/mysite/pages/create

如您所见,我们需要在多个路由中访问站点名称。

我们需要对所有这些操作执行相同的行为:

  1. 在当前帐户中查找具有给定标识符的网站
  2. 如果返回的站点为 null,则返回 404(或自定义视图)
  3. 如果站点不为空(有效),我们可以继续执行操作

我们始终可以通过 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 个左右的操作方法上执行此操作,因此希望尽可能保持 DRY。

我对自定义模型绑定器的工作并不多,所以我想知道这是否是一份更适合他们的工作。一个关键要求是我可以将我的依赖项注入模型绑定器(对于 ISiteContext 和 IRepository - 如果需要,我可以回退到 DependencyResolver)。

非常感谢,

本

更新

以下是使用自定义模型绑定器和操作过滤器的工作代码。我仍然不确定我对此有何感想,因为

  1. 我应该从模型绑定器访问我的数据库
  2. 我可以实际上从动作过滤器中检索对象和空值验证。哪个更好?

模型绑定器:

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

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-3 modelbinders action-filter


    【解决方案1】:

    这绝对听起来像是动作过滤器的工作。您可以使用操作过滤器进行 DI,这不是问题。

    是的,只需将您现有的功能转换为动作过滤器,然后将其应用于每个动作或控制器或您继承自的基本控制器。

    我不太了解您的网站是如何工作的,但您可以使用全局操作过滤器来检查特定路由值是否存在,例如'站点名称'。如果该路由值存在,则意味着您需要检查该站点是否存在...

    【讨论】:

    • 您是说不需要模型活页夹吗?我肯定需要这个才能从操作方法中访问 Site 对象吗?用于路由值检查的操作方法过滤器是一个好主意 - 我不能将其设为全局(因为并非所有操作都需要它),但它可以应用于某些控制器。
    • 不,一点也不。我只是满足了你的所有要求......我认为你没有提到你想要在你的操作方法中使用 Site 对象。如果您的操作中确实没有 Site 对象,那么这绝对是自定义模型绑定器的工作。我会注意到它们是单独的关注点,因此您仍然需要操作过滤器和自定义模型绑定器。
    • 我已经用“工作”代码和我的一些担忧更新了我的问题。你认为这是“好习惯”吗?从模型绑定器中访问我的数据库感觉有点不对劲。另外,我似乎可以从一个动作过滤器中完成这两项工作(因为我可以覆盖动作方法参数),但我不确定这是否更好。
    • 好问题。我个人在模型绑定器中访问数据库没有问题......它让事情变得容易多了。我还没有读过任何关于不访问数据库的论据——如果你能找到任何能给出令人信服的理由的东西,我会全力以赴。我在您的解决方案中唯一能看到的是UnitOfWork 属性...因为模型绑定将在OnActionExecuting 被触发之前发生,我猜这就是您的工作单元被实例化的地方。
    • 我最终重构了这个,以便我将一个 ISiteAccessor 注入到模型绑定器中。这将使我们更容易更改我们查找当前站点的方式(可能最终存储在会话中),而无需更改模型绑定器。无论如何,感谢您的帮助/澄清。
    【解决方案2】:

    对我来说,为您的站点类型定制模型绑定器听起来是个好主意。 您可能还需要一个操作过滤器来捕获“null”并返回未找到。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-10
      • 2015-03-01
      • 1970-01-01
      • 2011-06-10
      • 2021-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多