更好的内聚性:Asp.net MVC控制器文件夹的结构化

5
当构建复杂应用程序时,控制器可能变得笨重且非常大,这可能意味着您需要将它们拆分成单独的控制器。但这可能不适合,因为用户会在URI中看到控制器名称,从而影响用户体验。
例如:随MVC一起发货的默认项目具有AccountController,其中包含登录、注销、注册等操作,似乎违反了单一职责原则
那么问题是如何解决这个问题并分离出关注点?最初的回应可能只是创建单独的控制器。
AccountLoginController

AccountRegisterController

但是从客户的角度来看,这并不是一个很好的体验,因为它会影响请求资源时的URI。

一种解决方案是为每个控制器设置单独的文件夹,其中包含每个操作的单独类文件,每个类只有一个职责,如下所示。

Controllers (folder)
    Account  (folder)
        Register.cs
        Login.cs
        Logout.cs
    AnotherController (folder)
        Actionfile.cs
        Actionfile.cs

上述方法将功能分离,高内聚
所以,这是一个长的解释,但我的问题是...
- 有人之前实现过吗? - 如果是,你如何去做? - 对于这种模式,您有什么想法?

1
为什么要强调URI?大多数用户(99%以上)不会关心“.com”后面的任何内容。如果是“/Account/Register”或“/AccountRegister”,这有什么关系呢?我觉得很有趣,.net开发人员在每个MVC教程中都将路由作为第一步之前并不关心URIs。你去过eBay或亚马逊吗?那些URIs很糟糕,但它们仍然是成功的网站。你知道为什么吗?因为他们关心用户体验。我会花时间担心实际网站上的用户体验(功能、布局、菜单导航等),而不是用户在地址栏中看到的内容。/抱怨 - Ryan
4
@Ryan,我不明白你的评论是要作出怎样的贡献?声称.NET开发人员在关于URI上“并不在乎”,只是一种推测性的观点,而不是真正的事实。此外,这个问题并没有强调URI,而是关于分离关注点、单一职责和与ASP.NET框架设定的约定相一致的相关性的问题。 - Tomas Vinter
4个回答

3

你看过区域了吗?

"区域允许您将一个大项目组织成多个较小的部分,以便管理大型Web应用程序的复杂性。每个部分(“区域”)通常代表大型网站的单独部分,并用于分组相关的控制器和视图集。"

简而言之,区域是您项目中的一个文件夹,具有其自己的控制器、模型和视图的子文件夹。


2
这取决于你对单一职责的理解。如果你指的是认证作为一项职责,那么现成的控制器已经非常完美了。登录、注销和注册都是认证的一部分,因此将它们的代码放在同一个控制器中是有意义的。
如果你把单一职责原则推到极致,你永远不会有超过一个类、一个功能和一个文件的情况。你必须在可读性/可维护性和关注点分离之间找到平衡。在这种情况下,它走得太远了。
此外,要记住MVC完全遵循“约定优于配置”的原则,这意味着如果你根据约定命名控制器和视图,它们将是可发现的,并且你将减少配置和路由问题。
话虽如此,如果你决定采用非常规的控制器命名约定,你可以实现自己的控制器发现代码,从而允许你使用不同的约定。参考上面链接的文章。
public class ControllerConvention : TypeRules, ITypeScanner {
  public void Process(Type type, PluginGraph graph) {
     if (CanBeCast(typeof (IController), type)) {
     string name = type.Name.Replace("Controller", "").ToLower();
      graph.AddType(typeof(IController), type, name);
    }
  }
}

1
+1 - 你的内聚性和单一职责性如何,这是非常主观的。在我看来,提问者正在追逐一个梦想模式,而当前ASP.NET MVC的组织方式与其他所有MVC实现方式都非常相似。当你偏离了正道,你要么是个天才,要么就是完全疯了。由你决定。 ;) - John Farrell

0

你说得很有道理,但我认为这里的混淆在于控制器的真正含义。在MVC模式中,你的控制器可以被视为一组操作方法的命名空间。

让我们以.NET的“System”命名空间为例。 "System.Data"和"System.Core"有两个非常不同的职责,但它们都被分组在"System"包装器下。

当访问控制器的操作时,通常会使用路由,如{controller}/{action}。

"AccountController"中的"Account"部分映射到{controller},而"AccountController"中的方法映射到{action}。

我建议您构建一个类库来处理业务逻辑,并将您的操作路由到该库。您可以按照上面所述完全构建您的类,将操作的路由值传递给库并返回视图模型。

Register.cs可以是一个静态类,您可以从AccountController中的操作方法调用它。

例如:

"/account/register" ->

控制器:

public class AccountController : Controller
{
     public ActionResult Register()
     {
         return View("Register", Register.RegisterUser());
     }
}

控制器逻辑:

public static class Register
{
     public static RegisterModel RegisterUser()
     {
         // Handle application logic here and build your model
         return new RegisterModel() { PasswordLength = 12 };
     }
}

模型:

public class RegisterModel
{
    public int PasswordLength { get; set; }
}

0

约定优于配置非常适合快速搭建事物,或针对不需要长期考虑URI的后端应用程序。但是对于我在MVC上做的任何公共网站,都没有基于约定的路由被发布,原因如下:

  • 您的网址是您的API。 您应该控制它们并知道您在发布什么。
  • 我有点偏执,不喜欢通过URL诡计泄漏东西到我的应用程序中的可能性。

自定义路由还可以让您做一些事情,例如,/Foo可以根据操作同时转到FooFooController和FooBarController。 您甚至可以利用区域并将其暴露给公众。


1
我理解你的观点,但是在更大的MVC项目中,相反的情况适用。它们变得越大,我越感激这些约定,因为它们有助于防止可维护性问题。 - Daniel Dyson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接