ASP.NET MVC 最佳实践(一)

本系列翻译自 Kazi Manzur Rashid 的博客,由于翻译水平有限,本系列可能存在误解偏差或者翻译不准的地方,建议对比原文进行阅读。由于篇幅关系,原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第一部分中的1-6节。

1. 创建UrlHelper的扩展方法来生成url

尽量避免以字符串方式直接传递controller, action 或者 route name,最好是为创建为UrlHelper创建扩展方法来封装url。例如:

public static class UrlHelperExtension {
    public static string Home(this UrlHelper helper) {
        return helper.Content("~/");
    }

    public static string SignUp(this UrlHelper helper) {
        return helper.RouteUrl("Signup");
    }

    public static string Dashboard(this UrlHelper helper) {
        return Dashboard(helper, StoryListTab.Unread);
    }

    public static string Dashboard(this UrlHelper helper, StoryListTab tab) {
        return Dashboard(helper, tab, OrderBy.CreatedAtDescending, 1);
    }

    public static string Dashboard(this UrlHelper helper, StoryListTab tab, OrderBy orderBy, int page) {
        return helper.RouteUrl("Dashboard", new { tab = tab.ToString(), orderBy = orderBy.ToString(), page });
    }

    public static string Update(this UrlHelper helper) {
        return helper.RouteUrl("Update");
    }

    public static string Submit(this UrlHelper helper) {
        return helper.RouteUrl("Submit");
    }
}

这样,如果在你的视图中有类似这样的代码:

    <%= Html.ActionLink("Dashboard", "Dashboard", "Story") %>
    <a href="<%= Url.RouteUrl("Profile")%>">Profile</a>

你就可以用下面的这种方式来代替它们:

<a href="<%= Url.Dashboard() %>">Dashboard</a>
<a href="<%= Url.Profile() %>">Profile</a>

在控制器里也可以用,原来的代码:

return RedirectToAction(
    "Dashboard",
    "Story",
    new {
        tab = StoryListTab.Favorite,
        orderBy = OrderBy.CreatedAtAscending,
        page = 1
    }
);

可以写成:

return Redirect(Url.Dashboard(StoryListTab.Favorite,
                                OrderBy.CreatedAtAscending, 1));

当然了,你也可以使用 future assembly 中的强类型版本来获得控制器、方法以及参数值,或者创建你自己的强类型版本以免将来进行重构时太过于痛苦,但是请一定记住,它没有官方支持并且将来有可能会发生改变。上面的方式也可以和强类型版本搭配使用。当然“另外增加一个间接层”(Scott Ha 喜欢用的引语)有一些好处,在编写单元测试的时候还有一个好处是你只需处理 而无须同时处理 RedirectResult 和

2. Create Extension Method of UrlHelper to map your JavaScript, Stylesheet and Image Folder

默认情况下 创建Content、Scripts文件夹来做这些事,这一点我不喜欢,我更喜欢下面的这种文件夹结构,这样在IIS里我可以只让静态文件缓存在Assets文件夹而不是多个文件夹:

  • Assets
  • +images
  • +scripts
  • +stylesheets

无论是结构是什么样的,还是要为UrlHelper创建一些扩展方法来映射这些文件夹,以便你在视图中可以很方便地指向它们,这样以后如果你要改变目录结构,你就无须做大量查找替换的工作。我还建议你为所有经常在视图中用到的资源创建UrlHelper的扩展方法。例如:

public static string Image(this UrlHelper helper, string fileName) {
    return helper.Content("~/assets/images/{0}".FormatWith(fileName));
}

public static string Stylesheet(this UrlHelper helper, string fileName) {
    return helper.Content("~/assets/stylesheets/{0}".FormatWith(fileName));
}

public static string NoIcon(this UrlHelper helper) {
    return Image(helper, "noIcon.png");
}

然后当需要用到这些路径的时候,原来的代码

<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />

就可以写成这样:

<link href="<%= Url.Stylesheet("site.css")%>" rel="stylesheet" type="text/css" />

3. 中使用启动加载器

我在中已经提到过这一点,总的来说就是:如果你要在 Global.asax 的 Application_Start 中做很多处理,比如 注册路由规则(Routers)、注册控制器工厂(Controller Factory)、模型绑定(Model Binders)、视图引擎(View Engine)、启动程序级的特定后台服务或者为特定部分创建独立任务等,那就用来执行这些操作。这会让你的代码更简洁,更可测。这一点在用 构建门户级应用时尤其有用,因为这类应用中的每个模块都可能有一些不影响到其他模块的启动初始化操作。但是如果你只是开发一个小应用,上面提到的这些对你来说都不是问题,你当然也可以继续用默认的 global.asax。

4. 不要硬编码对依赖注入容器的调用,用通用服务定位器来代替。

不要让任何特定的依赖注入容器(DI Container)打乱你的代码,更好的办法是使用通用服务定位器(Common Service Locator),它是对底层依赖注入进行的抽象,已经支持几乎所有流行的依赖注入容器,它让你可以任意替换底层的依赖注入对象而无须修改代码,因为每个依赖注入容器都具有一些不同于其它容器的特性。Tim Barcz 最近写了一篇关于这个话题的很棒的文章,我不太明白他为什么没有提到我们对我们偏爱的依赖注入容器有多痴迷。通用服务定位器对绝大部分常用情景都提供了支持,而对一些特殊情况比如对已实体化对象的依赖注入,我印象里有 StructureMapNinject 和 Unity,你可以调用静态方法 ServiceLocator.Current.GetInstance 来代替。 通用服务定位器 是 Jeremy D Miller 发起的 依赖注入容器开发者(the DI Containers creators)的集体作品。

用通用服务定位器创建控制器工厂非常容易:

public class CommonServiceLocatorControllerFactory : DefaultControllerFactory {
    protected override IController GetControllerInstance(Type controllerType) {
        return (controllerType == null) ? base.GetControllerInstance(controllerType) : ServiceLocator.Current.GetInstance(controllerType) as IController;
    }
}

我希望 MVCContrib 的成员们也用这样的方式,而不是为每个容器创建一个独立的控制器工厂。

5. 用恰当的的AcceptVerbs属性来修饰你的控制器方法

比 Web Forms 更容易遭受攻击。所以务必让对数据进行修改的控制器方法只接受 Post 方式请求(HttpVerbs.Post)。如果安全对你来说至关重要,你还可以用 ValidateAntiForgeryToken 或者 Captcha。我强烈推荐 Derik Whittaker 写的一篇 很好的文章 以及视频介绍了怎么把 reCaptcha 整合到 应用程序(作者注:不要错过了 DimeCasts.net 的另一个短片,我从中学到了很多)。我的经验法则是对所有数据修改方法使用 HttpVerbs.Post,对所有数据读取操作使用 HttpVerbs.Get。

6. 用OutputCache修饰你的频繁调用的操作方法

当你需要返回不频繁更新的数据时使用 OutputCache 属性,最常见的例子是首页、Feed等。你在返回Html以及Json数据类型的方法中都可以使用这个属性。在使用的时候,注意只指定CacheProfile参数,不要指定任何其它东西,用 web.config 输出缓存控制节可以很好地调整它。例如:

[AcceptVerbs(HttpVerbs.Get), OutputCache(CacheProfile = "Dashboard")]
public ActionResult Dashboard(string userName, StoryListTab tab, OrderBy orderBy, int? page) {
}

在 web.config 文件中的配置:

<system.web>
    <caching>
        <outputCacheSettings>
            <outputCacheProfiles>
                <clear/>
                <!-- 15 Seconds -->
                <add
                    name="Dashboard"
                    duration="15"
                    varyByParam="*"
                    location="Client"
                    />
            </outputCacheProfiles>
        </outputCacheSettings>
    </caching>
</system.web>

请继续阅读:《

               

ASP.NET MVC 最佳实践(一)》上有3条评论

  1. Pingback引用通告: ASP.NET MVC 最佳实践(四) | 所谓技术 - 小李刀刀博客

  2. Pingback引用通告: ASP.NET MVC 最佳实践(三) | 所谓技术 - 小李刀刀博客

  3. Pingback引用通告: ASP.NET MVC 最佳实践(二) | 所谓技术 - 小李刀刀博客

评论已关闭。