Controllers frequently need to access incoming data, such as query string values, form values, and parameters parsed from the incoming URL by the routing system. There are three main ways to access that data:
控制器经常需要访问输入数据,如查询字串值、表单值、以及通过路由系统从输入URL解析所得到的参数。有三个主要的办法来访问这些数据:
Here, we’ll look at the approaches for getting input for your action methods, focusing on using context objects and action method parameters. In Chapter 17, we’ll cover model binding in depth.
以下,我们将考查为动作方法获取输入的途径,关注于使用上下文对象以及动作方法参数。在第17章中,我们将深度涉及模型绑定。
The most direct way to get hold of data is to fetch it yourself. When you create a controller by deriving from the Controller base class, you get access to a set of convenience properties to access information about the request. These properties include Request, Response, RouteData, HttpContext, and Server. Each provides information about a different aspect of the request. We refer to these as convenience properties, because they each retrieve different types of data from the request’s ControllerContext instance (which can be accessed through the Controller.ControllerContext property). We have described some of the most commonly used context objects in Table 12-1.
抓取数据最直接的办法是你自己去拿。当你的控制器是通过Controller基类派生而来的时候,你便得到了一组便利属性来访问与请求相关的信息。这些便利属性包括Request、Response、RouteData、HttpContext、以及Server。每一个都包含了请求的不同方面的信息。我们把这些称为便利属性,是因为它们每一个都从请求的ControllerContext实例(可以通过Controller.ControllerContext属性对它进行访问)接受了不同类型的数据。我们在表12-1中描述了一些最常用的上下文对象。
Property | Type | Description |
Request.QueryString | NameValueCollection | GET variables sent with this request |
Request.Form | NameValueCollection | POST variables sent with this request |
Request.Cookies | HttpCookieCollection | Cookies sent by the browser with this request |
Request.HttpMethod | string | The HTTP method (verb, such as GET or POST) used for this request |
Request.Headers | NameValueCollection | The full set of HTTP headers sent with this request |
Request.Url | Uri | The URL requested |
Request.UserHostAddress | string | The IP address of the user making this request |
RouteData.Route | RouteBase | The chosen RouteTable.Routes entry for this request |
RouteData.Values | RouteValueDictionary | Active route parameters (either extracted from the URL or default values) |
HttpContext.Application | HttpApplicationStateBase | Application state store |
HttpContext.Cache | Cache | Application cache store |
HttpContext.Items | IDictionary | State store for the current request |
HttpContext.Session | HttpSessionStateBase | State store for the visitor’s session |
User | IPrincipal | Authentication information about the logged-in user |
TempData | TempDataDictionary | Temporary data items stored for the current user |
An action method can use any of these context objects to get information about the request, as Listing12-5 demonstrates.
在一个动作方法中,可以用这些上下文对象的任意一个来获取与请求相关的信息,如清单12-5所示(有些未在表12-1中说明,比如Server — 译者注)。
Listing 12-5. An Action Method Using Context Objects to Get Information About a Request
清单12-5. 使用上下文对象获取请求相关信息的一个动作方法
public ActionResult RenameProduct() { // Access various properties from context objects string userName = User.Identity.Name; string serverName = Server.MachineName; string clientIP = Request.UserHostAddress; DateTime dateStamp = HttpContext.Timestamp; AuditRequest(userName, serverName,clientIP, dateStamp, "Renaming product"); // Retrieve posted data from Request.Form string oldProductName =Request.Form["OldName"]; string newProductName =Request.Form["NewName"]; bool result =AttemptProductRename(oldProductName, newProductName); ViewData["RenameResult"] =result; return View("ProductRenamed");}
You can explore the vast range of available request context information using IntelliSense (in an action method, type this. and browse the pop-up), and the Microsoft Developer Network(look up System.Web.Mvc.Controller and its base classes, or System.Web.Mvc.ControllerContext).
你可以用智能感应(在一个动作方法中,输入this.,并浏览弹出的内容)、和微软开发者网络(查看System.Web.Mvc.Controller及其基类,或System.Web.Mvc.ControllerContext)来浏览这些大量可用的请求上下文信息。
As you’ve seen in previous chapters, action methods can take parameters. This is a neater way to receive incoming data than extracting it manually from context objects, and it makes you action methods easier to read. For example, suppose we have an action method that uses context objects like this:
正如在前面一些章节你已经看到的,动作方法可以有参数。这是一种比通过上下文对象提取数据更灵活的接收输入数据的办法,而且这使你的动作方法更易于阅读。例如,假设我们有一个像下面这样使用上下文对象的动作方法:
public ActionResult ShowWeatherForecast(){ string city = RouteData.Values["city"]; DateTime forDate = DateTime.Parse(Request.Form["forDate"]); // ...implement weather forecast here ...}
We can rewrite it to useparameters, like this:
我们可以把它重写成使用参数的形式,像这样:
public ActionResult ShowWeatherForecast(stringcity, DateTime forDate){ // ...implement weather forecast here ...}
Not only is this easier to read, but it also helps with unit testing—we can unit test the action method without needing to mock the convenience properties of the controller class.
这不仅更易于阅读,而且它也有助于单元测试 — 我们不需要模仿控制器类的便利属性就能够单元测试这个动作方法。
For completeness, it’sworth noting that action methods aren’t allowed to have out or ref parameters. It wouldn’t make any sense if they did. ASP.NET MVC will simply throw an exception if it sees such a parameter.
出于完整性,值得注意的是,动作方法不允许有out或ref参数。如果这么做,没有任何意义。ASP.NET MVC如果看到这种参数会简单地弹出一个异常。
The MVC Framework will provide values for our parameters by checking context objects on our behalf, including Request.QueryString, Request.Form, and RouteData.Values. The names ofour parameters are treated case-insensitively, so that an action method parameter called city can be populated by a value from Request.Form["City"].
MVC框架将通过检查上下文对象的办法自动地给我们的参数提供值,包括Request.QueryString、Request.Form、以及RouteData.Values。对参数名的处理是大小写敏感的,以便一个名为city的动作方法参数能够被Request.Form["City"]的值所填充。
The base Controller class obtains values for your action method parameters using MVC Framework components called value providers and model binders.
底层Controller类使用叫做值提供器和模型绑定器的MVC框架组件,为你的动作方法参数获取值。
Value providers represent the set of data items available to your controller. There are built-in valueproviders that fetch items from Request.Form, Request.QueryString, Request.Files, and RouteData.Values. The values are then passed to model binders that try to map them to the types that your action methods require asparameters.
值提供器表现一组可用于控制器的数据项。有内建的值提供器,它们从Request.Form、Request.QueryString、Request.Files以及RouteData.Values获得各数据项。这些值然后被传递给模型绑定器,它试图把它们映射到你的动作方法参数的数据类型。
The default model binders can create and populate objects of any .NET type, including collections and project-specific custom types. You saw an example of this in Chapter 9 when form posts from administrators were presented to our action method as a single Product object, even though the individual values were dispersed among the elements of the HTML form.
默认的模型绑定器能够生成并填充任何.NET类型的对象,包括集合以及项目专用的自定义类型。你在第9章看到过它的一个例子,在管理员递交表单时,作为一个单一的Product对象被表现给了我们的动作方法,即使各个值被分散在HTML表单的各个元素之中也没问题。
We cover value providers and model binders in depth in Chapter 17.
我们将在第17章深度涉及值提供器和模型绑定器。
If the MVC Framework cannot find a value for a reference type parameter (such as a string or object), the action method will still be called, but using a null value for that parameter. If a value cannot be found for a value type parameter (such as int or double), then an exception will be thrown, and the action method will not be called. Here’s another way to think about it:
如果MVC框架找不到一个参考类型参数(如一个string或object)的值,动作方法仍然会被调用,但对该参数会使用一个null值。如果找不到一个值类型参数(如int或double)的值,那么会弹出一个异常,该动作方法不会被调用。以下是考虑了这一情况的另一种办法。
■ Note We are not referring to UI validation. If your goal is to provide the user with feedback about required form fields, see Chapter 18.
注:我们并不是在指UI校验。如果你的目的是给用户提供所需表单字段的反馈,请参阅第18章。
If you want to process requests that don’t contain values for action method parameters, but you would rather not check for null values in your code or have exceptions thrown, you can use the C# optional parameter feature instead. Listing 12-6 provides a demonstration.
如果你想处理不含动作方法参数值的请求,但你又不想在代码中检查null值或弹出异常,你可以代之以使用C#的可选参数特性。清单12-6提供了一个演示。
Listing 12-6. Using the C# Optional Parameter Feature in an Action Method
public ActionResult Search(string query= "all",int page = 1) { // ...}
We mark parameters as optional by assigning values when we define them. In the listing, we have provided default values for the query and page parameters. The MVC Framework will try to obtain values from the request for these parameters, but if there are no values available, the defaults we have specified will be used instead.
我们在定义参数时,通过对参数赋值的办法把参数标记为可选的。在上述清单中,我们给query和page参数提供了默认值。MVC框架将试图通过请求为这些参数获取值,但如果无值可用,那么将用我们所指定的默认值来替代。
For the string parameter, query, this means that we don’t need to check for null values. If the request we are processing didn’t specify a query, then our action method will be called with the string all. For the int parameter, we don’t need to worry about requests resulting in errors when there is no page value. Our method will be called with the default value of 1.
对于字符串型参数,query,这意味着我们不需要检查null值。如果我们所处理的请求没有查询字串,那么,我们的动作方法将用字符串all进行调用。对于int型参建,在没有page值时,我们不需要担心请求会导致错误。我们的方法将以默认值1进行调用。
Optional parameters can be used for literal types, which are any type that you can define without using the new keyword, such as string, int, and double.
可选参数可以被用于字面(literal)类型,所谓字面类型是不需要用new关键词定义的任何类型,如string、int、以及double等。
■ Caution If a request does contain a value for a parameter but it cannot be converted to the correct type (for example, if the user gives a nonnumeric string for an int parameter), then the framework will pass the default value for that parameter type (for example, 0for an int parameter), and will register the attempted value as a validation error in a special context object called ModelState. Unless you check for validation errors in ModelState, you can get into odd situations where the user has entered bad data into a form, but the request is processed as though the user had not entered any data or had entered the default value. See Chapter 18for details of validation and ModelState, which can be used to avoid such problems.
注意:如果一个请求包含了一个参数的值,但又不能把它转换成正确的类型(例如,如果用户对一个int参数给了一个非数值字符串),那么框架将给这个参数类型传递默认值(例如,对int参数给0值),并在一个名为ModelState的特殊的上下文对象中把这个尝试值注册为一个校验错误。除非你检查ModelState中的校验错误,否则你可能会陷入奇怪的境况,此时用户在表单中输入了错误数据,但该请求仍然被处理了,就好像用户还没有输入任何数据,或输入了默认数据。参阅第18章的校验和ModelState细节,可以用来避免这类问题。
联系客服