[转载]ASP.NET MVC的View是如何呈现出来的[实例篇]

[转载]ASP.NET MVC的View是如何呈现出来的[实例篇] – Artech – 博客园.

在《[设计篇]》篇中我们通过对View引擎的总体介绍讲述了从ViewResult的创建到View呈现的原理,为了让读者对View引擎及其View呈现机制具有一个深刻的认识,我们自定义一个简单的用于呈现静态HTML的StaticFileViewEngine。 在一个通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中,我们定义了如下一个针对于静态HTML内容呈现的自定义StaticFileView。 StaticFileView实现了IView接口,在实现的Render方法中读取制定文件的内容写入作为参数的TextWriter。 [本文已经同步到《How ASP.NET MVC Works?》中]

1: public class StaticFileView:IView

2: {

3: public string FileName { get; private set; }

4: public StaticFileView(string fileName)

5: {

6: this.FileName = fileName;

7: }

8: public void Render(ViewContext viewContext, TextWriter writer)

9: {

10: byte[] buffer;

11: using (FileStream fs = new FileStream(this.FileName, FileMode.Open))

12: {

13: buffer = new byte[fs.Length];

14: fs.Read(buffer, 0, buffer.Length);

15: }

16: writer.Write(Encoding.UTF8.GetString(buffer));

17: }

18: }

由于StaticFileView中定义的内容完全是静态的,所以缓存显得很有必要。我们只需要基于Controller和View名称对View实施缓存,为此我们定义了如下一个作为Key的数据类型ViewEngineResultCacheKey。

1: internal class ViewEngineResultCacheKey

2: {

3: public string ControllerName { get; private set; }

4: public string ViewName { get; private set; }

5:

6: public ViewEngineResultCacheKey(string controllerName, string viewName)

7: {

8: this.ControllerName = controllerName ?? string.Empty;

9: this.ViewName = viewName ?? string.Empty;

10: }

11: public override int GetHashCode()

12: {

13: return this.ControllerName.ToLower().GetHashCode() ^ this.ViewName.ToLower().GetHashCode();

14: }

15:

16: public override bool Equals(object obj)

17: {

18: ViewEngineResultCacheKey key = obj as ViewEngineResultCacheKey;

19: if (null == key)

20: {

21: return false;

22: }

23: return key.GetHashCode() == this.GetHashCode();

24: }

25: }

具有如下定义的StaticFileViewEngine代表StaticFileView对应的ViewEngine。我们通过一个字典类型的字段viewEngineResults作为对ViewEngineResult的缓存,而View的获取操作最终实现在InternalFindView方法中。通过StaticFileView表示的View定义在一个以View名称作为文件名的文本文件中,该文件的扩展名为.shtml(Static HTML)。

1: public class StaticFileViewEngine : IViewEngine

2: {

3: private DictionaryviewEngineResults = new Dictionary();

4: private object syncHelper = new object();

5: public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)

6: {

7: return this.FindView(controllerContext, partialViewName, null, useCache);

8: }

9:

10: public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)

11: {

12: string controllerName = controllerContext.RouteData.GetRequiredString("controller");

13: ViewEngineResultCacheKey key = new ViewEngineResultCacheKey(controllerName, viewName);

14: ViewEngineResult result;

15: if (!useCache)

16: {

17: result = InternalFindView(controllerContext, viewName, controllerName);

18: viewEngineResults[key] = result;

19: return result;

20: }

21: if(viewEngineResults.TryGetValue(key, out result))

22: {

23: return result;

24: }

25: lock (syncHelper)

26: {

27: if (viewEngineResults.TryGetValue(key, out result))

28: {

29: return result;

30: }

31:

32: result = InternalFindView(controllerContext, viewName, controllerName);

33: viewEngineResults[key] = result;

34: return result;

35: }

36: }

37:

38: private ViewEngineResult InternalFindView(ControllerContext controllerContext, string viewName, string controllerName)

39: {

40: string[] searchLocations = new string[]

41: {

42: string.Format( "~/views/{0}/{1}.shtml", controllerName, viewName),

43: string.Format( "~/views/Shared/{0}.shtml", viewName)

44: };

45:

46: string fileName = controllerContext.HttpContext.Request.MapPath(searchLocations[0]);

47: if (File.Exists(fileName))

48: {

49: return new ViewEngineResult(new StaticFileView(fileName), this);

50: }

51: fileName = string.Format(@"\views\Shared\{0}.shtml", viewName);

52: if (File.Exists(fileName))

53: {

54: return new ViewEngineResult(new StaticFileView(fileName), this);

55: }

56: return new ViewEngineResult(searchLocations);

57: }

58:

59: public void ReleaseView(ControllerContext controllerContext, IView view)

60: { }

61: }

在InternalFindView中,我们先在“~/Views/{ControllerName}/”目录下寻找View文件,如果不存在则在“~/Views/Shared/”寻找。如果对应View文件被找到,则以此创建一个StaticFileView对象,并最终返回封装该View对象的ViewEngineResult。如果目标View文件找不到,则根据基于这两个目录的搜寻地址列表创建并返回对应的ViewEngineResult。 现在我们在Global.asax通过如下的代码对自定义的StaticFileViewEngine进行注册,我们将创建的StaticFileViewEngine作为第一个使用的ViewEngine。

1: public class MvcApplication : System.Web.HttpApplication

2: {

3: protected void Application_Start()

4: {

5: //其他操作

6: ViewEngines.Engines.Insert(0, new StaticFileViewEngine());

7: }

8: }

然后我们定义了如下一个简单的HomeController,Action方法ShowNonExistentView中通过调用View方法呈现一个不存在的View(NonExistentView),而ShowStaticFileView方法则将对应的StaticFileView呈现出来。

1: public class HomeController : Controller

2: {

3: public ActionResult ShowNonExistentView()

4: {

5: return View("NonExistentView");

6: }

7:

8: public ActionResult ShowStaticFileView()

9: {

10: return View();

11: }

12: }

我们为Action方法ShowStaticFileView创建一个StaticFileView类型的View文件ShowStaticFileView.shtml(该View文件保存在“~/Views/Home”目录下,扩展名不是.cshtml,而是shtml),其内容就是如下一段完整的HTML。

1:

2:

3:

4:Static File View

5:

6:

7: 这是一个自定义的StaticFileView!

8:

9:

现在运行我们的程序,在浏览器中输入相应的地址访问Action方法ShowNonExistentView,会得到如下图所示的输出结果。图中列 出的View搜寻位置列表中的前两项正是我们自定义的StaticFileViewEngine寻找对应.shtml文件的两个地址。

 

image

 

如果我们改变浏览器的地址来访问另一个Action方法ShowStaticFileView,会呈现出如下图所示的输出结果,不难看出呈现出来的正是定义在ShowStaticFileView.shtml中的HTML。

 

image

ASP.NET MVC的View是如何被呈现出来的?[设计篇]

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏