新建立MVC3項(xiàng)目,名為12-1ControllersAndActions,使用空模板。
Global.asax中默認(rèn)的路由定義為:
public
static
void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
"
{resource}.axd/{*pathInfo}
"
);
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
, id = UrlParameter.Optional }
//
Parameter defaults
);
}
一、兩種方法實(shí)現(xiàn)自己的控制器
1、用IController創(chuàng)建控制器
在MVC框架中,控制器類(lèi)必須實(shí)現(xiàn)System.Web.Mvc命名空間的IController接口。
System.Web.Mvc.IController接口如下所示:
public
interface
IController
{
void
Execute(RequestContext requestContext);
}
接口只有一個(gè)方法Execute,在請(qǐng)求目標(biāo)控制器時(shí)將被調(diào)用。
通過(guò)實(shí)現(xiàn)IController,就可以創(chuàng)建控制器類(lèi),但這是一個(gè)相當(dāng)?shù)图?jí)的接口,要做大量工作才能讓自己創(chuàng)建的控制器有效,下面只是一個(gè)簡(jiǎn)單的演示。
鼠標(biāo)右擊項(xiàng)目中的Controllers文件夾,選擇 Add -> Class,創(chuàng)建新類(lèi),取名為BasicController,代碼如下:
namespace
_12_1ControllersAndActions.Controllers
{
public
class
BasicController:IController
{
public
void
Execute(RequestContext requestContext)
{
string
controller = (
string
)requestContext.RouteData.Values[
"
controller
"
];
string
action = (
string
)requestContext.RouteData.Values[
"
action
"
];
requestContext.HttpContext.Response.Write(
string
.Format(
"
Controller:{0}, Action:{1}
"
, controller, action));
}
}
}
?如果運(yùn)行程序,導(dǎo)航到"~/Basic/Index",根據(jù)路由定義,也可以導(dǎo)航到"~/Basic",產(chǎn)生的結(jié)果為:
Controller:Basic,Action:Index
?
2、一般的做法是創(chuàng)建派生于Controller類(lèi)的控制器
鼠標(biāo)右擊項(xiàng)目中的Controllers文件夾,選擇 Add -> Controller,新建控制器,命名為DerivedController,代碼如下:
namespace
_12_1ControllersAndActions.Controllers
{
public
class
DerivedController : Controller
{
//
//
GET: /Derived/
public
ActionResult Index()
{
ViewBag.Message
=
"
Hello from the DerivedController Index method.
"
;
return
View(
"
MyView
"
);
}
}
}
在方法Index上鼠標(biāo)右鍵,添加視圖,視圖取名為MyView
/Views/Derived/MyView.cshtml
@{
ViewBag.Title = "MyView";
}
<
h2
>
MyView
</
h2
>
<
h1
>
Message: @ViewBag.Message
</
h1
>
?運(yùn)行程序,導(dǎo)航到"~/Derived/Index",或者根據(jù)路由定義,也可以導(dǎo)航到"~/Derived",產(chǎn)生的結(jié)果為:
MyView
Message:Hello from the DerivedController Index method.
?
二、控制器接收輸入
控制器常常需要訪問(wèn)輸入數(shù)據(jù),也就是在請(qǐng)求控制器的動(dòng)作方法時(shí)傳遞進(jìn)來(lái)的輸入數(shù)據(jù),如查詢(xún)字符串值(指url尾部帶問(wèn)號(hào)?后面跟的部分,稱(chēng)為url的查詢(xún)字符串)、表單值、以及路由系統(tǒng)根據(jù)輸入url解析所得到的參數(shù)。
訪問(wèn)這些數(shù)據(jù)有三種方式:
通過(guò)上下文對(duì)象(Context Objects)獲取數(shù)據(jù)。
通過(guò)參數(shù)傳遞給動(dòng)作方法。
使用模型綁定(Model Binding)。
這里主要討論前兩種,模型綁定在后面討論。
1、通過(guò)上下文對(duì)象獲取數(shù)據(jù)
訪問(wèn)上下文對(duì)象,通過(guò)使用一組便利屬性(Convenience Property)來(lái)進(jìn)行訪問(wèn)。所謂上下文對(duì)象,實(shí)際上就是訪問(wèn)的與請(qǐng)求相關(guān)的信息。
如果要使用便利屬性來(lái)訪問(wèn)與請(qǐng)求相關(guān)的信息,要注意一點(diǎn),就是只能在派生于Controller的自定義控制器中才能使用。即,如果是通過(guò)實(shí)現(xiàn)IController接口來(lái)完成的自定義控制器里面不能使用這些便利屬性。這些便利屬性包括Request、Response、RouteData、HttpContext、Server等,每個(gè)屬性都包含了請(qǐng)求不同方面的信息。見(jiàn)p305,表12-1。
(1)下面通過(guò)一個(gè)例子來(lái)表現(xiàn)這種通過(guò)上下文對(duì)象獲取數(shù)據(jù)的方式。
在12-1ControllersAndActions項(xiàng)目中,HomeController控制器定義如下:
namespace
_12_1ControllersAndActions.Controllers
{
public
class
HomeController : Controller
{
public
ActionResult Index()
{
//
訪問(wèn)上下文對(duì)象的各個(gè)屬性
string
userName =
User.Identity.Name;
string
serverName =
Server.MachineName;
string
clientIP =
Request.UserHostAddress;
DateTime dateStamp
=
HttpContext.Timestamp;
ViewBag.userName
=
userName;
ViewBag.serverName
=
serverName;
ViewBag.clientIP
=
clientIP;
ViewBag.dateStamp
=
dateStamp;
//
接收Request.Form所遞交的數(shù)據(jù)
//
string oldProductName = Request.Form["OldName"];
//
string newProductName = Request.Form["NewName"];
return
View();
}
}
}
?
/Views/Home/Index.cshtml內(nèi)容如下:
@{
ViewBag.Title
=
"
Index
"
;
}
<h2>Index</h2>
<h2>userName=@ViewBag.userName</h2>
<h2>serverName=@ViewBag.serverName</h2>
<h2>clientIP=@ViewBag.clientIP</h2>
<h2>dataStamp=@ViewBag.dateStamp</h2>
這里可以注意,如果希望顯示出一些上下文數(shù)據(jù),而又不想有對(duì)應(yīng)的cshtml文件,那么可以用response.write,例如:
namespace
_12_1ControllersAndActions.Controllers
{
public
class
HomeController : Controller
{
public
ActionResult Index()
{
//
訪問(wèn)上下文對(duì)象的各個(gè)屬性
string
userName =
User.Identity.Name;
string
serverName =
Server.MachineName;
string
clientIP =
Request.UserHostAddress;
DateTime dateStamp
=
HttpContext.Timestamp;
ViewBag.userName
=
userName;
ViewBag.serverName
=
serverName;
ViewBag.clientIP
=
clientIP;
ViewBag.dateStamp
=
dateStamp;
//
接收Request.Form所遞交的數(shù)據(jù)
//
string oldProductName = Request.Form["OldName"];
//
string newProductName = Request.Form["NewName"];
return
View();
}
public
void
Index2()
{
//
訪問(wèn)上下文對(duì)象的各個(gè)屬性
string
userName =
User.Identity.Name;
string
serverName =
Server.MachineName;
string
clientIP =
Request.UserHostAddress;
DateTime dateStamp
=
HttpContext.Timestamp;
Response.Write(
string
.Format(
"
userName:{0}<br>serverName:{1}<br>clientIP:{2}<br>dataStamp:{3}
"
,
userName, serverName, clientIP, dateStamp));
}
}
}
這里的Index2就沒(méi)有對(duì)應(yīng)的cshtml文件對(duì)應(yīng)。程序運(yùn)行后,輸入地址:"~/Home/Index2"就可以查看到用Response.Write寫(xiě)出的內(nèi)容。
?
?上下文對(duì)象常用的有Request.QueryString、Request.Form和RouteData.Values
(2)通過(guò)上下文對(duì)象訪問(wèn)RouteData.Values的例子
上面例子中的項(xiàng)目12-1ControllersAndActions,在HomeController控制器中添加了一個(gè)TestInput動(dòng)作方法,如下:
namespace
_12_1ControllersAndActions.Controllers
{
public
class
HomeController : Controller
{
public
ActionResult Index()
{
//
訪問(wèn)上下文對(duì)象的各個(gè)屬性
string
userName =
User.Identity.Name;
string
serverName =
Server.MachineName;
string
clientIP =
Request.UserHostAddress;
DateTime dateStamp
=
HttpContext.Timestamp;
ViewBag.userName
=
userName;
ViewBag.serverName
=
serverName;
ViewBag.clientIP
=
clientIP;
ViewBag.dateStamp
=
dateStamp;
//
接收Request.Form所遞交的數(shù)據(jù)
//
string oldProductName = Request.Form["OldName"];
//
string newProductName = Request.Form["NewName"];
return
View();
}
public
void
Index2()
{
//
訪問(wèn)上下文對(duì)象的各個(gè)屬性
string
userName =
User.Identity.Name;
string
serverName =
Server.MachineName;
string
clientIP =
Request.UserHostAddress;
DateTime dateStamp
=
HttpContext.Timestamp;
Response.Write(
string
.Format(
"
userName:{0}<br>serverName:{1}<br>clientIP:{2}<br>dataStamp:{3}
"
,
userName, serverName, clientIP, dateStamp));
}
public
void
TestInput()
{
string
inputController = (
string
)RouteData.Values[
"
controller
"
];
string
inputAction = (
string
)RouteData.Values[
"
action
"
];
int
inputId = Convert.ToInt32(RouteData.Values[
"
id
"
]);
Response.Write(
string
.Format(
"
inputController={0}<br>inputAction={1}<br>inputId={2}
"
,
inputController, inputAction, inputId));
}
}
}
在TestInput動(dòng)作方法中,通過(guò)RouteData.Values["controller"]讀取到當(dāng)前請(qǐng)求的控制器名字,通過(guò)RouteData.Values["action"]讀取到當(dāng)前請(qǐng)求的動(dòng)作方法的名字,通過(guò)RouteData.Values["id"]訪問(wèn)到當(dāng)前請(qǐng)求中的 URL里對(duì)應(yīng)到路由中的自定義變量id的值讀取出來(lái)。那么這里RouteData.Values中的controller、action、id屬性來(lái)自于哪里,RouteData.Values中還有哪些屬性可用,就取決于在Global.asax中的路由定義。
先看一下為當(dāng)前這個(gè)例子設(shè)計(jì)的路由定義:
public
static
void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
"
{resource}.axd/{*pathInfo}
"
);
//
這是本身的默認(rèn)路由,現(xiàn)在需要如果有id要限定它只能是數(shù)字,用正則表達(dá)式
//
routes.MapRoute(
//
"Default",
//
Route name
//
"{controller}/{action}/{id}",
//
URL with parameters
//
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//
Parameter defaults
//
);
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id=
@"
\d+
"
}
);
}
現(xiàn)在這個(gè)例子,希望路由在原來(lái)的默認(rèn)路由的基礎(chǔ)上增加一個(gè)約束,就是如果url中輸入了id,那么希望將id的值約束在數(shù)字上,如果id輸入的是非數(shù)字的值,比如字母之類(lèi)就不能匹配路由。
用正則表達(dá)式加約束條件,生成匿名對(duì)象new { id=@"\d+" },但是這個(gè)約束不能直接加在原來(lái)的默認(rèn)路由上,原來(lái)的默認(rèn)路由為:
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
, id = UrlParameter.Optional }
//
Parameter defaults
);
在這個(gè)默認(rèn)路由中,定義了url模式里的變量有3個(gè),分別是controller、action和id,并用new設(shè)置了這三個(gè)變量的默認(rèn)參數(shù)值,其中id設(shè)置的是UrlParameter.Optional,設(shè)置為這個(gè)值,表示在匹配url時(shí),id可以有也可以沒(méi)有,如果沒(méi)有id,那么就沒(méi)有id這個(gè)變量。如果希望有id的時(shí)候把它的值限定在數(shù)字上,不能直接在參數(shù)默認(rèn)值的后面添加約束,比如:
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
, id = UrlParameter.Optional },
//
Parameter defaults
new
{ id=
@"
\d+
"
}
);
加上這個(gè)約束,就表示id必須要有值,而且要是數(shù)字,否則就不匹配。這樣一來(lái),下面的url都不能再匹配了:
"~/"
"~/Home/Index"
"~/Home/TestInput"
也就是說(shuō)id=UrlParameter.Optional就失去了意義。
解決方法就是把路由定義成兩個(gè):
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id=
@"
\d+
"
}
);
按照順序依次匹配,沒(méi)有寫(xiě)id的匹配第一個(gè)路由定義,寫(xiě)了id的匹配第二個(gè)路由定義。
那么沒(méi)有寫(xiě)id的時(shí)候,匹配第一個(gè)路由定義,就是{controller}/{action},就只有controller和action兩個(gè)變量,這個(gè)時(shí)候在動(dòng)作方法中訪問(wèn)RouteData.Values["id"]得到的結(jié)果就為null,但是這里的類(lèi)型轉(zhuǎn)換用的是Convert.ToInt32(),當(dāng)括號(hào)內(nèi)的對(duì)象為null時(shí),得到的結(jié)果就為0。如果用的是int.Parse()遇到這種情況就會(huì)拋出異常。
int inputId = Convert.ToInt32(RouteData.Values["id"]);
當(dāng)然,如果取到id的值,如果只想要字符類(lèi)型,那就不用這么復(fù)雜, 可以直接用
string inputId = (string)RouteData.Values["id"];
?
對(duì)于本例,如果在url中輸入的是"~/Home/TestInput/325",那么顯示的結(jié)果為:
inputController=Home
inputAction=TestInput
inputId=325
?
(3)通過(guò)上下文對(duì)象訪問(wèn)Request.QueryString的例子
Request.QueryString查詢(xún)字符串就是跟在url后面帶問(wèn)號(hào)之后的內(nèi)容。例如:
http://localhost:1943/Home/TestInput2?var1=abc&var2=123
問(wèn)號(hào)后面的var1=abc&var2=123就是QueryString。
?
例,假設(shè)跟上一個(gè)例題同樣的路由定義:
public
static
void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
"
{resource}.axd/{*pathInfo}
"
);
//
這是本身的默認(rèn)路由,現(xiàn)在需要如果有id要限定它只能是數(shù)字,用正則表達(dá)式
//
routes.MapRoute(
//
"Default",
//
Route name
//
"{controller}/{action}/{id}",
//
URL with parameters
//
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//
Parameter defaults
//
);
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id=
@"
\d+
"
}
);
}
?
在HomeController中新增了名為T(mén)estInput2的動(dòng)作方法:
public
void
TestInput2()
{
string
inputController = (
string
)RouteData.Values[
"
controller
"
];
string
inputAction = (
string
)RouteData.Values[
"
action
"
];
int
inputId = Convert.ToInt32(RouteData.Values[
"
id
"
]);
string
queryVar1 = Request.QueryString[
"
var1
"
];
string
queryVar2 = Request.QueryString[
"
var2
"
];
Response.Write(
string
.Format(
"
inputController={0}<br>inputAction={1}<br>
"
+
"
inputId={2}<br>queryVar1={3}<br>queryVar2={4}
"
,
inputController, inputAction, inputId, queryVar1, queryVar2));
}
這里用了兩句話
string queryVar1 = Request.QueryString["var1"];
string queryVar2 = Request.QueryString["var2"];
來(lái)讀取查詢(xún)字符串中變量的值。
如果輸入的url是"~/Home/TestInput2?var1=abc&var2=123"則顯示的結(jié)果為:
inputController=Home
inputAction=TestInput2
inputId=0
queryVar1=abc
queryVar2=123
這個(gè)輸入的url,沒(méi)有匹配id,所以RouteData.Values["id"]為空,經(jīng)過(guò)Convert.ToInt32()轉(zhuǎn)換后值為0。QueryString里如果有多個(gè)變量,之間用符號(hào)&間隔。
?
擴(kuò)充下這個(gè)例題,現(xiàn)在假設(shè)在/Views/Home/Index.cshtml,也就是HomeController中Index產(chǎn)生的視圖上添加:
@Html.ActionLink("Navigate", "TestInput2", new { id="123" })
這時(shí),如果路由定義為:
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
, id = UrlParameter.Optional }
//
Parameter defaults
);
那么ActionLink產(chǎn)生的html為:
<a href="/Home/TestInput2/123">Navigate</a>
點(diǎn)擊該超鏈接后顯示的結(jié)果為:
inputController=Home
inputAction=TestInput2
inputId=123
queryVar1=
queryVar2=
?
但是,如果路由定義為:
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id =
@"
\d+
"
}
);
那么@Html.ActionLink("Navigate", "TestInput2", new { id="123" })產(chǎn)生的html為:
<a href="/Home/TestInput2?id=123">Navigate</a>
這是因?yàn)榘炊x順序,會(huì)首先去匹配第一個(gè)路由定義,那么反推回url。第一個(gè)路由定義中沒(méi)有id,那就認(rèn)為new { id="123" }產(chǎn)生的就是查詢(xún)字符串。點(diǎn)擊這個(gè)超鏈接后,產(chǎn)生的顯示結(jié)果為:
inputController=Home
inputAction=TestInput2
inputId=0
queryVar1=
queryVar2=
?
這樣一來(lái),如果是第二種路由定義,既想生成的url中產(chǎn)生id,又有查詢(xún)字符串,那就需要使用:
@Html.ActionLink("Navigate", "TestInput2/123", new { var1="ABC", var2="325" })
產(chǎn)生的html為:
<a href="/Home/TestInput2/123?var1=ABC&var2=325">Navigate</a>
注意html中的&就是&,最后產(chǎn)生的url就是"~/Home/TestInput2/123?var1=ABC&var2=325"
點(diǎn)擊該超鏈接后,產(chǎn)生的顯示結(jié)果為:
inputController=Home
inputAction=TestInput2
inputId=123
queryVar1=ABC
queryVar2=325
?
(4)通過(guò)上下文對(duì)象訪問(wèn)Request.Form中數(shù)據(jù)的例子
利用Request.Form可以讀取提交過(guò)來(lái)的表單中的數(shù)據(jù),通過(guò)Name的屬性值來(lái)進(jìn)行識(shí)別訪問(wèn)。下面構(gòu)造一個(gè)例子來(lái)說(shuō)明用Request.Form來(lái)讀取表單數(shù)據(jù)的情況。
假設(shè)使用的路由定義為:
public
static
void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
"
{resource}.axd/{*pathInfo}
"
);
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id =
@"
\d+
"
}
);
}
在HomeController中添加了TestInput3()動(dòng)作方法的兩個(gè)重載版本:
[HttpGet]
public
ViewResult TestInput3()
{
return
View();
}
[HttpPost]
public
ViewResult TestInput3(
string
dummy)
{
string
inputController = (
string
)RouteData.Values[
"
controller
"
];
string
inputAction = (
string
)RouteData.Values[
"
action
"
];
int
inputId = Convert.ToInt32(RouteData.Values[
"
id
"
]);
string
city = Request.Form[
"
City
"
];
DateTime forDate
= DateTime.Parse(Request.Form[
"
forDate
"
]);
ViewBag.inputController
=
inputController;
ViewBag.inputAction
=
inputAction;
ViewBag.inputId
=
inputId;
ViewBag.city
=
city;
ViewBag.forDate
=
forDate;
return
View(
"
TI3Result
"
);
}
這里用了兩個(gè)注解屬性[HttpGet]和[HttpPost]來(lái)指定一個(gè)TestInput3用于Get請(qǐng)求,另一個(gè)TestInput3動(dòng)作方法用于Post請(qǐng)求。在本例中,希望在Post請(qǐng)求的TestInput3動(dòng)作方法中利用上下文對(duì)象Request.Form來(lái)訪問(wèn)表單數(shù)據(jù),本部需要指定參數(shù)。但是同名的兩個(gè)動(dòng)作方法TestInput3看來(lái)是通過(guò)重載來(lái)實(shí)現(xiàn)的,如果名字相同,返回類(lèi)型相同,參數(shù)又完全一致的話,程序就不能通過(guò)編譯。所以,為了演示這個(gè)例子,就在Post請(qǐng)求的動(dòng)作方法TestInput3上加了一個(gè)啞元參數(shù)dummy,目的就是為了完成同名的TestInput3函數(shù)的重載。
接下來(lái),在由Get請(qǐng)求TestInput3時(shí),返回的視圖是默認(rèn)視圖/Views/Home/TestInput3.cshtml,在里面構(gòu)造了表單:
@{
ViewBag.Title = "TestInput3";
}
<
h2
>
TestInput3
</
h2
>
@using (Html.BeginForm())
{
<
p
>
city:
<
input
type
="text"
name
="City"
/></
p
>
<
p
>
forDate:
<
input
type
="text"
name
="forDate"
/></
p
>
<
input
type
="submit"
value
="提交"
/>
}
表單由
@using (Html.BeginForm())
{
? ? ...
}
指定。里面有是三個(gè)元素,第一個(gè)是文本框,name為City,第二個(gè)也是文本框,name屬性為forDate。Request.Form就依賴(lài)這些元素的name來(lái)識(shí)別和訪問(wèn)指定的元素值。第三個(gè)元素是按鈕,類(lèi)型為submit,按鈕顯示的文字為“提交”。點(diǎn)擊該按鈕后,默認(rèn)將表單Post到與產(chǎn)生當(dāng)前視圖同名的動(dòng)作方法上,本例子中,產(chǎn)生這個(gè)視圖的動(dòng)作方法是TestInput3,那么Post回去的時(shí)候,也就是傳遞給同名的TestInput3動(dòng)作方法。
在響應(yīng)Post的TestInput3動(dòng)作方法通過(guò):
string city = Request.Form["City"];
DateTime forDate = DateTime.Parse(Request.Form["forDate"]);
讀取到表單中元素的值后,再利用ViewBag傳遞給顯示結(jié)果的視圖,該動(dòng)作方法返回時(shí)指定了視圖名return View("TI3Result").
那么,就在/Views/Home/TI3Result.cshtml中產(chǎn)生輸出顯示的結(jié)果。添加視圖文件/Views/Home/TI3Result.cshtml如下:
@{
ViewBag.Title = "TI3Result";
}
<
h2
>
TI3Result
</
h2
>
<
p
>
inputController=@ViewBag.inputController
</
p
>
<
p
>
inputAction=@ViewBag.inputAction
</
p
>
<
p
>
inpuId=@ViewBag.inputId
</
p
>
<
p
>
city=@ViewBag.city
</
p
>
<
p
>
forDate=@ViewBag.forDate
</
p
>
執(zhí)行程序后,在url上輸入"~/Home/TestInput3,顯示為:
?在文本框中輸入數(shù)據(jù)如下:
點(diǎn)擊提交按鈕后,顯示結(jié)果為:
?
2、為動(dòng)作方法設(shè)定參數(shù)傳遞數(shù)據(jù)
上下文對(duì)象常用的Request.QueryString、Request.Form和RouteData.Values等數(shù)據(jù)也可以通過(guò)動(dòng)作方法的參數(shù)來(lái)設(shè)定。這里有個(gè)約定,就是參數(shù)名跟要訪問(wèn)的屬性名或元素名同名,系統(tǒng)是根據(jù)名字自動(dòng)去匹配。
(1)先看一個(gè)常規(guī)的例子,假設(shè)路由定義為:
public
static
void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
"
{resource}.axd/{*pathInfo}
"
);
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
, id = UrlParameter.Optional }
//
Parameter defaults
);
}
在HomeController中新加了名為T(mén)estInput4()的動(dòng)作方法:
public
void
TestInput4(
int
id)
{
Response.Write(
string
.Format(
"
id={0}
"
, id));
}
在動(dòng)作方法TestInput()上設(shè)定了參數(shù),整型的名為id的參數(shù)。動(dòng)作方法的參數(shù)會(huì)用名字自動(dòng)去匹配Request.QueryString、Request.Form和RouteData.Values中的屬性和元素的值,依賴(lài)的就是名字。
接下來(lái),在/Views/Home/Index.cshtml中添加ActionLink來(lái)生成超鏈接,指向HomeController中的TestInput4()動(dòng)作方法。添加的代碼為:
@Html.ActionLink("NavTestInput4", "TestInput4", new{ id=325 })
請(qǐng)注意ActionLink是怎么根據(jù)路由定義來(lái)生成html的。
第一個(gè)參數(shù)"NavTestInput4"是超鏈接文本,第二個(gè)參數(shù)是要訪問(wèn)的動(dòng)作方法名字。沒(méi)有給出控制器名字,則默認(rèn)為產(chǎn)生當(dāng)前視圖頁(yè)面的控制器,在本例子中就是Home控制器。第三個(gè)參數(shù)用new生成匿名對(duì)象,其中有id=325,根據(jù)路由定義,id匹配路由模式中的id變量。倒退回url,生成的超鏈接為:
<
a
href
="/Home/TestInput4/325"
>
NavTestInput4
</
a
>
點(diǎn)擊該超鏈接后,根據(jù)路由定義,匹配路由模式"{controller}/{action}/{id}",訪問(wèn)到Home控制器中的TestInput4動(dòng)作方法,動(dòng)作方法的參數(shù)id匹配路由模式中的{id},也就是RouteData.Values["id"]就通過(guò)匹配的動(dòng)作方法參數(shù)id被傳遞到了動(dòng)作方法中。而且可以看到,類(lèi)型也自動(dòng)匹配,參數(shù)中的int id,不需要做任何類(lèi)型那個(gè)轉(zhuǎn)換。顯示結(jié)果為:
id=325
(2)跟上一個(gè)例題一樣的路由定義,現(xiàn)在將HomeController中的TestInput4修改為:
public
void
TestInput4(
int
id,
string
var1)
{
Response.Write(
string
.Format(
"
id={0}<br>var1={1}
"
, id, var1));
}
也就是說(shuō)TestInput4的參數(shù)可以去匹配Request.QueryString、Request.Form和RouteData.Values等數(shù)據(jù)中的id和var1的值。將/Views/Home/Index.cshtml中ActionLink修改為:
@Html.ActionLink("NavTestInput4", "TestInput4", new{ id=325, var1="ABC" })
根據(jù)路由定義,在ActionLink中的第三個(gè)參數(shù)new{ id=325, var1="ABC" },id匹配路由模式"{controller}/{action}/{id}"中的{id},而var1在路由模式中沒(méi)有變量叫這個(gè)名字,那就以問(wèn)號(hào)?跟在url的最后面作為QueryString。本例子中的ActionLink產(chǎn)生的html為:
<
a
href
="/Home/TestInput4/325?var1=ABC"
>
NavTestInput4
</
a
>
點(diǎn)擊該超鏈接后,訪問(wèn)到Home控制器中的TestInput4動(dòng)作方法。TestInput4動(dòng)作方法中的參數(shù)id,接收RouteData.Values["id"]的值,參數(shù)var1接收Request.QueryString["var1"]的值。顯示的結(jié)果為:
id=325
var1=ABC
(3)注意路由變化,如果將路由定義改為:
public
static
void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
"
{resource}.axd/{*pathInfo}
"
);
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id =
@"
\d+
"
}
);
}
假設(shè)HomeController中的動(dòng)作方法TestInput4()與第(1)個(gè)例子中一樣:
public
void
TestInput4(
int
id)
{
Response.Write(
string
.Format(
"
id={0}
"
, id));
}
在/Views/Home/Index.cshtml中的ActionLink也與第(1)個(gè)例子一樣,代碼為:
@Html.ActionLink("NavTestInput4", "TestInput4", new{ id=325 })
注意,由于路由的不同,這個(gè)例子中ActionLink生成html就與第(1)個(gè)例子不一樣了。根據(jù)本例子中的路由定義,按照順序,匹配第一個(gè)路由。在第一個(gè)路由中,路由的url模式為"{controller}/{action}",路由模式中沒(méi)有id變量。那么ActionLink的第三個(gè)參數(shù)new{ id=325 }中的id就沒(méi)有路由模式中的變量與其對(duì)應(yīng),就只能跟在問(wèn)號(hào)后面,放在url的最后作為QueryString。所以,本例中生成的html為:
<
a
href
="/Home/TestInput4?id=325"
>
NavTestInput4
</
a
>
需要注意的是,雖然產(chǎn)生的url不一樣,但是在訪問(wèn)Home控制器里的TestInput4動(dòng)作方法時(shí),仍然可以讓TestInput4的參數(shù)id正確讀取到url中的id的值。這就是與前面直接通過(guò)上下文對(duì)象RouteData.Values["id"],或Request.QueryString["id"]來(lái)訪問(wèn)id值不一樣的地方。動(dòng)作方法TestInput4中的參數(shù)id,會(huì)自動(dòng)去匹配Request.QueryString、Request.Form和RouteData.Values中與參數(shù)同名的數(shù)據(jù)。所以本例子中TestInput4的參數(shù)id,接收的是Request.QueryString["id"]的值,顯示的結(jié)果為:
id=325
RouteData.Values和Request.QueryString中如果都有與動(dòng)作方法的參數(shù)相同的變量,優(yōu)先匹配的是RouteData.Values。例如,假設(shè)在Index.cshtml中的ActionLink代碼為:
@Html.ActionLink(
"
NavTestInput4
"
,
"
TestInput4/123
"
,
new
{ id=
325
})
路由匹配本例子中的第二個(gè)路由定義,路由模式為"{controller}/{action}/{id}",產(chǎn)生的html為:
<
a
href
="/Home/TestInput4/123?id=325"
>
NavTestInput4
</
a
>
根據(jù)匹配的第二個(gè)路由,這里既有RouteData.Values["id"]值為123,又有Request.QueryString["id"]值為325,傳遞給Home控制器的動(dòng)作方法TestInput4的時(shí)候,TestInput4的參數(shù)id優(yōu)先接收RouteData.Values["id"],顯示結(jié)果為:
id=123
(4)提交的表單,Request.Form中各元素的值也可以通過(guò)動(dòng)作方法的參數(shù)傳遞。
例如,前面TestInput3的例子,其他不變,將HomeController中接收Post請(qǐng)求的TestInput3動(dòng)作方法修改為:
[HttpPost]
public
ViewResult TestInput3(
string
controller,
string
action,
string
city, DateTime forDate,
int
id =
0
)
{
string
inputController =
controller;
string
inputAction =
action;
int
inputId =
id;
ViewBag.inputController
=
inputController;
ViewBag.inputAction
=
inputAction;
ViewBag.inputId
=
inputId;
ViewBag.city
=
city;
ViewBag.forDate
=
forDate;
return
View(
"
TI3Result
"
);
}
效果跟上一個(gè)TestInput3的例子一樣。而且forDate的類(lèi)型自動(dòng)就給轉(zhuǎn)換為DateTime類(lèi)型。
?
三、從控制器產(chǎn)生輸出
1、不要視圖,直接用Response.Write輸出
只要是派生于Controller的類(lèi)里面的動(dòng)作方法,就可以直接用Response.Write().
例如,前面的例子Index2()
namespace
_12_1ControllersAndActions.Controllers
{
public
class
HomeController : Controller
{
public
void
Index2()
{
//
訪問(wèn)上下文對(duì)象的各個(gè)屬性
string
userName =
User.Identity.Name;
string
serverName =
Server.MachineName;
string
clientIP =
Request.UserHostAddress;
DateTime dateStamp
=
HttpContext.Timestamp;
Response.Write(
string
.Format(
"
userName:{0}<br>serverName:{1}<br>clientIP:{2}<br>dataStamp:{3}
"
,
userName, serverName, clientIP, dateStamp));
}
}
}
以及直接用Response.Redirect("/Some/Other/Url")也是屬于這一種。
例如,在HomeController中添加動(dòng)作方法TestRe
public
void
TestRe()
{
Response.Redirect(
"
/Home/Index
"
);
}
執(zhí)行后,若輸入"~/Home/TestRe",則會(huì)自動(dòng)轉(zhuǎn)移到"~/Home/Index"
?
2、理解Action Result
在動(dòng)作方法中不直接使用Response對(duì)象,而是返回一個(gè)派生于ActionResult類(lèi)的對(duì)象,它描述控制器要完成的操作,例如產(chǎn)生一個(gè)視圖、重定向到另一個(gè)url或動(dòng)作方法等。
不同的操作用不同的派生類(lèi),它們都是ActionResult的派生類(lèi)。例如,重定向:
public
ActionResult TestRe()
{
return
new
RedirectResult(
"
/Home/Index
"
);
}
結(jié)果就返回RedirectResult的一個(gè)對(duì)象,因?yàn)镽edirectResult派生于ActionResult,所以動(dòng)作方法的返回類(lèi)型可以用ActionResult,當(dāng)然也可以明確使用RedirectResult作為該動(dòng)作方法的返回類(lèi)型。另外,各派生類(lèi)也有一些控制器輔助器方法,可以簡(jiǎn)化調(diào)用,例如RedirectResult類(lèi)有輔助器方法Redirect
public
ActionResult TestRe()
{
return
Redirect(
"
/Home/Index
"
);
}
跟上面直接使用return new RedirectResult("/Home/Index");產(chǎn)生的效果是一樣的。
各個(gè)常用的派生類(lèi)和用到的控制器輔助方法見(jiàn)p313。
?
3、從動(dòng)作方法中產(chǎn)生視圖作為輸出
public
class
HomeController : Controller
{
public
ViewResult Index()
{
return
View();
}
}
產(chǎn)生視圖 /Views/Home/Index.cshtml
再如
public
class
HomeController : Controller
{
public
ViewResult Index()
{
return
View(
"
HomePage
"
);
}
}
產(chǎn)生的視圖為 /Views/Home/HomePage.cshtml
return View("HomePage")參數(shù)里加上雙引號(hào),表示給出指定的視圖名。就不是默認(rèn)跟動(dòng)作方法同名的視圖了。
另外,這里動(dòng)作方法的返回類(lèi)型用的是ViewResult,因?yàn)樵谥婪椒ǚ祷氐念?lèi)型時(shí),傾向于使用具體的類(lèi)型,當(dāng)然直接使用ActionResult也可以的。MVC框架在搜索視圖時(shí),先搜索Areas再搜索Views。僅以cshtml為例,下面是搜索順序:
/Areas/<AreaName>/Views/<ControllerName>/視圖名.cshtml
/Areas/<AreaName>/Views/Shared/視圖名.cshtml
/Views/<ControllerName>/視圖名.cshtml
/Views/Shared/視圖名.cshtml
?
視圖文件在生成html時(shí)會(huì)用到/Views/Shared/_Layout.cshtml布局文件作為默認(rèn)布局文件,如果要用另一個(gè)布局文件可以用?
return
View(
"
HomePage
"
,
"
_OtherLayout);
?當(dāng)然,先要保證這個(gè)布局文件在/Views/Shared/目錄中,也就是/Views/Shared/_OtherLayout.cshtml
?
四、把數(shù)據(jù)從動(dòng)作方法傳遞給視圖
1、使用視圖模型對(duì)象
@model 類(lèi)型
在HomeController中添加動(dòng)作方法VMO(),如下:
public
ViewResult VMO()
{
DateTime date
=
DateTime.Now;
return
View(date);
}
注意,這里的return View(date);參數(shù)里的date沒(méi)有加雙引號(hào),這表示要傳遞給視圖的數(shù)據(jù),而不是指定要渲染的視圖名,這里如果將date加上雙引號(hào),含義就變了,就表示該動(dòng)作方法要產(chǎn)生一個(gè)名為date.cshtml的視圖來(lái)進(jìn)行顯示。
這里使用return View(date);就表示把對(duì)象date傳遞到與當(dāng)前動(dòng)作方法同名的視圖上,也就是/Views/Home/VMO.cshtml
@model DateTime
@{
ViewBag.Title
=
"
VMO
"
;
}
<h2>VMO</h2>
the day
is
:@Model.DayOfWeek
在開(kāi)頭指定模型類(lèi)型時(shí),要用小寫(xiě)的m,這里是@model DateTime。而在文中讀取模型值時(shí),要用大寫(xiě)的M,如這里的@Model.DayOfWeek.
剛才強(qiáng)調(diào),在動(dòng)作方法中,返回View帶參數(shù)時(shí),不要加雙引號(hào),才表示返回的數(shù)據(jù)對(duì)象。如果要直接返回字符串對(duì)象,就需要在前面加上(object)指明這是模型對(duì)象。
public
ViewResult VMO()
{
DateTime date
=
DateTime.Now;
return
View((
object
)
"
hello, world.
"
);
}
在VMO.cshtml中,就使用string來(lái)指定模型類(lèi)型。
@model string
@{
ViewBag.Title = "VMO";
}
<
h2
>
VMO
</
h2
>
the day is:@Model
2、使用ViewBag傳遞數(shù)據(jù)
ViewBag允許你在這個(gè)動(dòng)態(tài)對(duì)象上定義任意屬性,并在視圖中訪問(wèn)它們 ,就相當(dāng)于鍵/值對(duì)。
只是vs對(duì)它不提供智能感應(yīng)支持。
?
3、執(zhí)行重定向
(1)重定向到字面url
假設(shè)在HomeController中有動(dòng)作方法TestRe
public
RedirectResult TestRe()
{
return
Redirect(
"
/Home/Index
"
);
}
當(dāng)訪問(wèn)"~/Home/TestRe"時(shí),就會(huì)重定向到"~/Home/Index"
(2)重定向到路由系統(tǒng)的url
在HomeController中,假設(shè)有前面例子中的動(dòng)作方法TestInput2()
public
void
TestInput2()
{
string
inputController = (
string
)RouteData.Values[
"
controller
"
];
string
inputAction = (
string
)RouteData.Values[
"
action
"
];
int
inputId = Convert.ToInt32(RouteData.Values[
"
id
"
]);
string
queryVar1 = Request.QueryString[
"
var1
"
];
string
queryVar2 = Request.QueryString[
"
var2
"
];
Response.Write(
string
.Format(
"
inputController={0}<br>inputAction={1}<br>
"
+
"
inputId={2}<br>queryVar1={3}<br>queryVar2={4}
"
,
inputController, inputAction, inputId, queryVar1, queryVar2));
}
下面定義動(dòng)作方法TestRe()來(lái)重定向到TestInput2()
public
RedirectToRouteResult TestRe()
{
return
RedirectToRoute(
new
{
Controller
=
"
Home
"
,
Action
=
"
TestInput2
"
,
id
=
"
123
"
,
var1
=
"
ABC
"
,
var2
=
"
999
"
});
}
執(zhí)行程序后,當(dāng)輸入"~/Home/TestTe",將產(chǎn)生重定向。注意,產(chǎn)生的url取決于所使用的路由定義。如果路由定義為
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
, id = UrlParameter.Optional }
//
Parameter defaults
);
則重定向產(chǎn)生的url為
"~/Home/TestInput2/123?var1=ABC&var2=999"
但這個(gè)路由定義,如果沒(méi)有加以處理,在id處如果輸入的不是數(shù)字,那么將會(huì)拋出異常。
如果路由定義用的是下面的定義
routes.MapRoute(
"
Default
"
,
//
Route name
"
{controller}/{action}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
}
//
Parameter defaults
);
routes.MapRoute(
"
Default2
"
,
//
Route name
"
{controller}/{action}/{id}
"
,
//
URL with parameters
new
{ controller =
"
Home
"
, action =
"
Index
"
},
//
Parameter defaults
new
{ id =
@"
\d+
"
}
);
那么產(chǎn)生的重定向url為
"~/Home/TestInput2?id=123&var1=ABC&var2=999"
(3)重定向到一個(gè)動(dòng)作方法
public
RedirectToRouteResult TestRe()
{
return
RedirectToAction(
"
TestInput2
"
);
}
?
public
RedirectToRouteResult TestRe()
{
return
RedirectToAction(
"
TestInput2
"
,
new
{ id=
"
123
"
, var1=
"
ABC
"
, var2=
"
999
"
});
}
?
public
RedirectToRouteResult TestRe()
{
return
RedirectToAction(
"
TestInput2
"
,
"
Home
"
);
}
?
public
RedirectToRouteResult TestRe()
{
return
RedirectToAction(
"
TestInput2
"
,
"
Home
"
,
new
{ id=
"
123
"
, var1=
"
ABC
"
, var2=
"
999
"
});
}
?
?4、返回文件及二進(jìn)制數(shù)據(jù)
(1)返回文件
文件下載
public
FileResult TestFile()
{
string
fPath = AppDomain.CurrentDomain.BaseDirectory +
"
DownloadTest/
"
;
//
string fileName = @"c:\log.txt";
string
fileName = fPath +
"
log.txt
"
;
string
contentType =
"
text/plain
"
;
string
downloadName =
"
Test.txt
"
;
return
File(fileName, contentType, downloadName);
}
這里的AppDomain.CurrentDomain.BaseDirectory表示讀取到當(dāng)前項(xiàng)目的根物理路徑,末尾帶反斜杠。要下載的文件log.txt放在根目錄下的DownloadTest文件夾中。在出現(xiàn)另存為對(duì)話框的時(shí)候,下載名被改為T(mén)est.txt。
(2)發(fā)送字節(jié)數(shù)組
public
FileContentResult TestFile()
{
byte
[] data = ...
//
二進(jìn)制內(nèi)容
return
File(data,
"
text/plain
"
,
"
Test.txt
"
);
}
(3)發(fā)送流內(nèi)容
如果所處理的數(shù)據(jù)可以通過(guò)一個(gè)打開(kāi)的System.IO.Stream進(jìn)行操作,可以把這個(gè)流傳遞給File方法的一個(gè)重載版本。這個(gè)流得內(nèi)容將被讀取并發(fā)送給瀏覽器。
public
FileStreamResult TestFile()
{
Stream stream
= ...
//
打開(kāi)某種流
return
File(stream,
"
text/html
"
);
}
5、返回錯(cuò)誤及http錯(cuò)誤代碼
(1)指定錯(cuò)誤碼
public
HttpStatusCodeResult StatusCode()
{
return
new
HttpStatusCodeResult(
404
,
"
url cannot be serviced.
"
);
}
?
(2)發(fā)送404錯(cuò)誤
public
HttpStatusCodeResult StatusCode()
{
return
HttpNotFound();
}
?
(3)發(fā)送401錯(cuò)誤
public
HttpStatusCodeResult StatusCode()
{
return
new
HttpUnauthorizedResult();
}
?
?
-lyj
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

