您的位置:首页 > 编程语言 > ASP

七天学会ASP.NET MVC (六)——线程问题、异常处理、自定义URL 【转】

2016-05-18 14:07 435 查看

http://www.cnblogs.com/powertoolsteam/p/MVC_Six.html



本节又带了一些常用的,却很难理解的问题,本节从文件上传功能的实现引出了线程使用,介绍了线程饥饿的解决方法,异常处理方法,了解RouteTable自定义路径。

系列文章

七天学会ASP.NETMVC(一)——深入理解ASP.NETMVC

七天学会ASP.NETMVC(二)——ASP.NETMVC数据传递

七天学会ASP.NETMVC(三)——ASP.NetMVC数据处理

七天学会ASP.NETMVC(四)——用户授权认证问题

[b]七天学会ASP.NETMVC(五)——Layout页面使用和用户角色管理
[/b]

[b][b]七天学会ASP.NETMVC(六)——线程问题、异常处理、自定义URL[/b][/b]

七天学会ASP.NETMVC(七)——创建单页应用

[b]目录[/b]

[b]实验27——添加批量上传选项[/b]

[b]关于实验27[/b]

[b]实验27存在的问题[/b]

[b]解决方法[/b]

[b]实验28——解决线程饥饿问题[/b]

[b]实验29——异常处理—显示自定义错误页面[/b]

[b]关于实验29[/b]

[b]理解实验29中的限制[/b]

[b]实验30—异常处理—日志异常[/b]

[b]关于实验30[/b]

理解RouteTable

理解Asp.netMVC请求周期

实验31—实现用户友好URLs

关于实验31

[b]总结[/b]

[b]实验27——添加批量上传选项[/b]

在实验27中,我们将提供一个选项,供用户选择上传Employee记录文件(CSV格式)。

我们会学习以下知识:

1.如何使用文件上传控件

2.异步控制器

1.创建FileUploadViewModel

在ViewModels文件夹下新建类“FileUploadViewModel”,如下:


[code]

[/code]
HttpPostedFileBase将通过客户端提供上传文件的访问入口。

2.创建BulkUploadController和Indexaction方法

新建controller“BulkUploadController”,并实现IndexAction方法,如下:


[code]

[/code]
Index方法与HeaderFooterFilter和AdminFilter属性绑定。HeaderFooterFilter会确保页眉和页脚数据能够正确传递到ViewModel中,AdminFilter限制非管理员用户的访问。
3.创建上传View

创建以上Action方法的View。View名称应为index.cshtml,且存放在“~/Views/BulkUpload”文件夹下。

4.设计上传View

在View中输入以下内容:


[code]

[/code]
如上,FileUploadViewModel中属性名称与input[type="file"]的名称类似,都称为“fileUpload”。我们在ModelBinder中已经讲述了名称属性的重要性,注意:在表单标签中,有一个额外的属性是加密的,会在实验结尾处讲解。

5.创建业务层上传方法

在EmployeeBusinessLayer中新建方法UploadEmployees,如下:


[code]

[/code]
6.创建UploadAction方法

创建Action方法,并命名为“BulkUploadController”,如下:


[code]

[/code]
AdminFilter会绑定到Uploadaction方法中,限制非管理员用户的访问。

7.创建BulkUpload链接

打开“Views/Employee”文件夹下的AddNewLink.cshtml文件,输入BulkUpload链接,如下:
<ahref="/Employee/AddNew">AddNew</a>

<ahref="/BulkUpload/Index">BulkUpload</a>

8.运行

8.1创建一个样本文件来测试,如图所示



8.2运行,点击BulkUpload链接



选择文件并点击确认



关于实验27

为什么在实验27中不需要验证?

在该选项中添加客户端和服务器端验证需要读者自行添加的,以下是添加验证的提示:

服务器端验证可使用DataAnnotations。

客户端验证可利用客户端的数据解释和执行jQuery的验证。必须手动设置自定义数据属性,因为并没有将Htmlhelper方法设置为文件输入。

客户端验证可编写JavaScript代码,通过点击按钮来实现。这个方法并不是很难,由于文件输入是由输入控件完成,值可以在JavaScript中获取及验证。

什么是HttpPostedFileBase?

HttpPostedFileBase将通过客户端提供文件上传的访问入口,ModelBinder会在Post请求期间更新
FileUploadViewModel类中的所有属性值。我们在FileUploadViewModel内部只有一个属性,Model
Binder会通过客户端设置它实现文件上传。

是否会提供多文件的输入控件?

是,有两种方法可以实现:

1.创建多文件输入控件,每个控件有唯一的名称,FileUploadViewModel类会为每个控件创建HttpPostedFileBase类型的属性,每个属性名称应该与控件名称匹配。

2.创建多文件输入控件,每个控件有相同的名称,创建类型的List列表,代替创建多个HttpPostedFileBase类型的属性。

enctype="multipart/form-data"是用来做什么的?

该属性指定了post数据的编码类型,默认属性值是”application/x-www-form-urlencoded“

例1—登录窗体会给服务器发送以下Post请求


[code]

[/code]
所有输入值会被作为发送的值的一部分,以”key/value“的形式发送。

当enctype="multipart/form-data"属性被加入Form标签中,以下post请求会被发送到服务器。


[code]

[/code]
如上所示,Form会在多部分post发送,每部分都是被分界线分割的,每部分包含单值。

如果form标签包含文件输入控件的话,enctype必须被设置为”multipart/form-data“。

为什么有时候需要设置encType为“multipart/form-data”,而有时候不需要设置?

当encType设置为”multipart/form-data“,将会实现Post数据和上传文件的功能,当然也会增加请求的size增加,请求size越大意味着性能越低。因此得出的最佳实践经验需要设置为默认的”application/x-www-form-urlencoded“。

为什么在实验27中创建ViewModel?

在View中已经有一个控件了,我们需要通过直接添加HttpPostedFileBase类型的参数,并命名为”fileUpload“实现相同的结果,从而替代创建独立的ViewModel。


[code]

[/code]
创建ViewModel是最好的方法,Controller应该以ViewModel的形式给View发送数据,且数据必须来自Controller。

以上问题的解决方法

是否存在疑虑,当发送请求时,如何获取响应?

众人皆知的编程规则,程序中任何事件都是由线程执行的,请求事件也是。

Asp.netframework维护线程池,每次当请求发送到webserver时,会从线程池中分配空闲的线程处理此请求。这种线程被称为worker线程。



当请求处理完成,该线程无法服务其他请求时,worker线程会被阻塞。现在我们来了解什么是线程饥饿,如果一个应用程序接收到很多请求,且处理每个请求都非常耗时。在这种情况下,我们就必须指定一个点来结束请求,当有新的请求进入状态时,没有worker线程可使用,这种现象称为线程饥饿。

在我们的示例程序中只包含2个员工记录,而在实际使用情况下,会包含成千上万的记录,这就意味着将耗费大量的时间来处理请求。这种情况就可能导致线程饥饿.

线程饥饿的解决方法:

截至现在我们讨论的请求类型都是同步请求。如果使用异步请求来代替同步请求,那么线程饥饿的问题就得到解决了。

异步请求的情况下,会分配worker线程来服务请求。

worker线程初始化异步操作,并返回到线程池服务其他请求。异步操作可使用CLR线程来继续执行。

存在的问题就是,CLR线程无法返回响应,一旦它完成了异步操作,它会通知Asp.net。

Webserver再次获取一个worker线程来处理剩余的请求,并返回响应。

上述使用场景中,会获取两次worker线程,这两次获取的线程可能相同,也可能会不同。

文件读取是I/O操作,不需要使用worker线程处理。因此最好将同步请求转换为异步。

同步请求的响应时间能提升吗?

不可以,响应时间是相同的,线程会被释放来服务其他请求。

实验28——解决线程饥饿问题

在Asp.netMVC中会通过将同步Action方法转换为异步Action方法,将同步请求转换为异步请求。

1.创建异步控制器

在控制器中将基类UploadController修改为AsynController。


[code]

[/code]
2.转换同步Action方法

该功能通过两个关键字就可实现:“async“和”await”


[code]

[/code]
在action方法的开始或结束处,使用变量存储线程ID。

理一下思路:

当上传按钮被点击时,新请求会被发送到服务器。

Webserver从线程池中产生Worker线程,并分配给服务器请求。

worker线程会使Action方法执行

Worker方法在Task.Factory.StartNew方法的辅助下,开启异步操作

使用async关键字将Action方法标记为异步方法,由此会保证异步操作一旦开启,Worker线程就会释放。

使用await关键字也可标记异步操作,能够保证异步操作完成时才能够继续执行下面的代码。

一旦异步操作在Action方法中完成执行,必须执行worker线程。因此webserver将会新建一个空闲worker线程,并用来服务剩下的请求,提供响应。

3.测试运行

运行应用程序,并跳转到BulkUpload页面。会在代码中显示断点,输入样本文件,点击上传。



如图所示,在项目启动或关闭时有的线程ID是不同的。

实验29——异常处理—显示自定义错误页面

如果一个项目不考虑异常处理,那么可以说这个项目是不完整的。到目前为止,我们已经了解了MVC中的两个过滤器:Actionfilter和Authorizationfilter。现在我们来学习第三个过滤器,异常过滤器(ExceptionFilters)。

什么是异常过滤器(ExceptionFilters)?

异常过滤器与其他过滤器的用法相同,可当作属性使用。使用异常过滤器的基本步骤:

1.使它们可用

2.将过滤器作为属性,应用到action方法或控制器中。我们也可以在全局层次使用异常过滤器。

异常过滤器的作用是什么?,是否有自动执行的异常过滤器?

一旦action方法中出现异常,异常过滤器就会控制程序的运行过程,开始内部自动写入运行的代码。MVC为我们提供了编写好的异常过滤器:HandeError。

当action方法中发生异常时,过滤器就会在“~/Views/[currentcontroller]”或“~/Views/Shared”目录下查找到名称为”Error”的View,然后创建该View的ViewResult,并作为响应返回。

接下来我们会讲解一个Demo,帮助我们更好的理解异常过滤器的使用。

已经实现的上传文件功能,很有可能会发生输入文件格式错误。因此我们需要处理异常。

1.创建含错误信息的样本文件,包含一些非法值,如图,Salary就是非法值。



2.运行,查找异常,点击上传按钮,选择已建立的样本数据,选择上传。



3.激活异常过滤器

当自定义异常被捕获时,异常过滤器变为可用。为了能够获得自定义异常,打开Web.config文件,在System.Web.Section下方添加自定义错误信息。


[code]
[/code]
4.创建ErrorView

在“~/Views/Shared”文件夹下,会发现存在“Error.cshtml”文件,该文件是由MVC模板提供的,如果没有自动创建,该文件也可以手动完成。


[code]

[/code]
5.绑定异常过滤器

将过滤器绑定到action方法或controller上,不需要手动执行,打开App_Startfolder文件夹中的FilterConfig.cs文件。在RegisterGlobalFilters方法中会看到HandleError过滤器已经以全局过滤器绑定成功。


[code]

[/code]
如果需要删除全局过滤器,那么会将过滤器绑定到action或controller层,但是不建议这么做,最好是在全局中应用如下:


[code]

[/code]
6.运行



7.在View中显示错误信息

将ErrorView转换为HandleErrorInfo类的强类型View,并在View中显示错误信息。


[code]

[/code]
8.运行测试



Handleerror属性能够确保无论是否出现异常,自定义View都能够显示,但是它的能力在controller和action方法中是受限的。不会处理“Resourcenotfound”这类型的错误。

运行应用程序,输一些奇怪的URL



9.创建ErrorController控制器,并创建Index方法,代码如下:


[code]

[/code]
10.在非法URL中显示自定义Error视图

可在web.config中定义“Resourcenotfounderror”的设置,如下:


[code]

[/code]
11.使ErrorController全局可访问。

将AllowAnonymous属性应用到ErrorController中,因为错误控制器和index方法不应该只绑定到认证用户,也很有可能用户在登录之前已经输入错误的URL。


[code]

[/code]
12.运行



关于实验29

View的名称是否可以修改?

可以修改,不一定叫Error,也可以指定其他名字。如果ErrorView的名称改变了,当绑定HandleError过滤器时,必须制定View的名称。


[code]

[/code]
是否可以为不同的异常获取不同的ErrorView?

可以,在这种情况下,必须多次应用Handleerrorfilter。


[code]

[/code]
前两个Handleerrorfilter都指定了异常,而最后一个更为常见更通用,会显示所有其他异常的ErrorView。

上述实验中并没有处理登录异常,我们会在实验30中讲解登录异常。

实验30——异常处理—登录异常

1.创建Logger类

在根目录下,新建文件夹,命名为Logger。在Logger文件夹下新建类FileLogger


[code]

[/code]
2.创建EmployeeExceptionFilter类

在Filters文件夹下,新建EmployeeExceptionFilter类


[code]

[/code]
3.扩展HandleError实现登录异常处理

让EmployeeExceptionFilter继承HandleErrorAttribute类,重写OnException方法:


[code]

[/code]
4.定义OnException方法

在OnException方法中包含异常登录代码。


[code]

[/code]
5.修改默认的异常过滤器

打开FilterConfig.cs文件,删除HandErrorAtrribute,添加上步中创建的。


[code]

[/code]
6.运行

会在C盘中创建“Error”文件夹,存放一些error文件。





关于实验30

当异常出现后,ErrorView是如何返回响应的?

查看OnException方法的最后一行代码:



即基类的OnException方法执行并返回ErrorView的ViewResult。

在OnException中,是否可以返回其他结果?

可以,代码如下:


[code]

[/code]
当返回自定义响应时,做的第一件事情就是通知MVC引擎,手动处理异常,因此不需要执行默认的操作,不会显示默认的错误页面。使用以下语句可完成:



Routing

到目前为止,我们已经解决了MVC的很多问题,但忽略了最基本的最重要的一个问题:当用户发送请求时,会发生什么?

最好的答案是“执行Action方法”,但仍存在疑问:对于一个特定的URL请求,如何确定控制器和action方法。在开始实验31之前,我们首先来解答上述问题,你可能会困惑为什么这个问题会放在最后来讲,因为了解内部结构之前,需要更好的了解MVC。

理解RouteTable

在Asp.netmvc中有RouteTable这个概念,是用来存储URL路径的,简而言之,是保存已定义的应用程序的可能的URLpattern的集合。

默认情况下,路径是项目模板组成的一部分。可在Global.asax文件中检查到,在Application_Start中会发现以下语句:



App_Start文件夹下的RouteConfig.cs文件,包含以下代码块:


[code]

[/code]

RegisterRoutes方法已经包含了由routes.MapRoute方法定义的默认的路径。已定义的路径会在请求周期中确定执行的是正确的控制器和action方法。如果使用route.MapRoute创建了多个路径,那么内部路径的定义就意味着创建Route对象。

MapRoute方法也可与RouteHandler关联。

理解ASP.NETMVC请求周期

在本节中我们只讲解请求周期中重要的知识点

1.UrlRoutingModule

当最终用户发送请求时,会通过UrlRoutingModule对象传递,UrlRoutingModule是HTTP模块。

2.Routing

UrlRoutingModule会从routetable集合中获取首次匹配的Route对象,为了能够匹配成功,请求URL会与route中定义的URLpattern匹配。

当匹配的时候必须考虑以下规则:

数字参数的匹配(请求URL和URLpattern中的数字)



URLpattern中的可选参数:



参数中定义的静态参数



3.创建MVCRouteHandler

一旦Route对象被选中,UrlRoutingModule会获得Route对象的MvcRouteHandler对象。

4.创建RouteData和RequestContext

UrlRoutingModule使用Route对象创建RouteData,可用于创建RequestContext。RouteData封装了路径的信息如Controller名称,action名称以及route参数值。

Controller名称

为了从URL中获取Controller名称,需要按规则执行如在URLpattern中{Controller}是标识Controller名称的关键字。

ActionMethod名称

为了获取action方法名称,{action}是标识action方法的关键字。

Route参数

URLpattern能够获得以下值:

1.{controller}

2.{action}

3.字符串,如“MyCompany/{controller}/{action}”,“MyCompany”是字符串。

4.其他,如“{controller}/{action}/{id}”,”id“是路径的参数。

例如:

Routepattern->“{controller}/{action}/{id}”

请求URL->http://localhost:8870/BulkUpload/Upload/5

测试1


[code]

[/code]
测试2


[code]

[/code]
测试3


[code]

[/code]

5.创建MVCHandler

MvcRouteHandler会创建MVCHandler的实例传递RequestContext对象

6.创建Controller实例

MVCHandler会根据ControllerFactory的帮助创建Controller实例

7.执行方法

MVCHandler调用Controller的执行方法,执行方法是由Controller的基类定义的。

8.调用Action方法

每个控制器都有与之关联的ControllerActionInvoker对象。在执行方法中ControllerActionInvoker对象调用正确的action方法。

9.运行结果

Action方法会接收到用户输入,并准备好响应数据,然后通过返回语句返回执行结果,返回类型可能是ViewResult或其他。

实验31——实现对用户有好的URL

1.重新定义RegisterRoutes方法

RegisterRoutes方法中包含additionalroute


[code]

[/code]
2.修改URL引用

打开“~/Views/Employee”文件下的AddNewLink.cshtml,修改BulkUpload链接,如下:


[code]
[/code]
3.运行测试



关于实验31

之前的URL现在是否起作用?

是,仍然有用。BulkUploadController中的Index方法可通过两个URL访问。

1.”http://localhost:8870/Employee/BulkUpload“

2.“http://localhost:8870/BulkUpload/Index”

Route参数和Query字符串有什么区别?

Query字符串本身是有大小限制的,而无法定义Route参数的个数。

无法在Query字符串值中添加限制,但是可以在Route参数中添加限制。

可能会设置Route参数的默认值,而QueryString不可能有默认值。

Query字符串可使URL混乱,而Route参数可保持它有条理。

如何在Route参数中使用限制?

可使用正则表达式。

如:


[code]

[/code]
Action方法:


[code]

[/code]
为了保证每个路径参数都能独立,因此参数名称必须与RouteParameter一致。

是否需要将action方法中的参数名称与Route参数名称保持一致?

RoutePattern也许会包含一个或多个RouteParameter,为了区分每个参数,必须保证action方法的参数名称与Route参数名称相同。

定义路径的顺序重要吗?

有影响,在上面的实验中,我们定义了两个路径,一个是自定义的,一个是默认的。默认的是最先定义的,自定义路径是在之后定义的。

当用户输入“http://.../Employee/BulkUpload”地址后发送请求,UrlRoutingModule会搜索与请求URL匹配的默认的routepattern,它会将Employee作为控制器的名称,“BulkUpload”作为action方法名称。因此定义的顺序是非常重要的,更常用的路径应放在最后。

是否有什么简便的方法来定义Action方法的URLpattern?

我们可使用基于routing的属性。

1.基本的routing属性可用

在RegisterRoutes方法中在IgnoreRoute语句后输入代码如下:


[code]

[/code]
2.定义action方法的routepattern


[code]

[/code]
3.运行测试



routing属性可定义route参数,如下:


[code]
[/code]
IgnoreRoutes的作用是什么?

当我们不想使用routing作为特别的扩展时,会使用IgnoreRoutes。作为MVC模板的一部分,在RegisterRoute方法中下列语句是默认的:



这就是说如果用户发送以“.axd”为结束的请求,将不会有任何路径加载的操作,请求将直接定位到物理资源。

总结

6天的MVC学习已经完成了,希望大家能够将所讲的知识充分理解,充分吸收。第7章我们会使用MVC,JQUery和Ajax创建简单的页面应用。欢迎大家持续关注!

知识只是一部分力量,强有力的工具也是帮助我们开发的重要部分。

使用ComponentOneStudioEnterprise中提供的ComponentOneStudioASP.NETMVC版本,您能获取快速的轻量级控件来满足用户所有需求。

原文链接:http://www.codeproject.com/Articles/1002109/Learn-MVC-Project-in-days-Day-6
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: