ASP.NET MVC 框架入门——写一个搜索引擎
动态网页的历史非常悠久,可以追溯到上个世纪。就技术类型而言,主要有ASP、PHP、JSP三大派。笔者接触过ASP、PHP,遗憾的是几乎从未接触过JSP。偶就天生不是JAVA语系的。
后来,笔者稍微远离了一下Web开发,Web发生了翻天覆地的变化,css成了布局主流,ASP.NET冒出来了。这使得笔者不得不在2008、2009年间重新进入Web开发领域。然后,自认为已经跟上时代了,于是又稍微远离了Web开发。短短两三年,PHP几乎啥也没变,HTML基本未变(虽然冒出了个HTML5),但ASP.NET却发生了翻天覆地的变化:官方MVC框架的推出,使得ASP.NET再也不用使用很别扭的服务端控件了,将自由操控HTML的权利还给了开发者(然而,却剥夺了自由操控URL的权利)。
下面,笔者以写一个搜索引擎为例,带大家进入ASP MVC 框架的世界。
建立项目
如下图,在VS2012中建立一个ASP.NET MVC4 Web Application:
然后选Basic吧:
试图引擎按默认选择Razor吧。
建好之后,我们将看到如下的项目结构:
其中Views放各种页面,Content放css啥的,Controllers放“控制器”代码,Models里放各种业务模型的数据结构定义。
URL规划
这个MVC框架的一个越俎代庖的事情就是接管了URL路由。嗯,其实也蛮方便的,叫越俎代庖有点过了,仁者见仁智者见智吧。
打开RouteConfig.cs,我肯可以看到:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } } |
这里有个默认的路由。比如一段http://www.xxx.com/a/b/c/,按照这个route的解释,a就是controller的名字,b就是action的名字,c就是id的名字。如果访问http://www.xxx.com/a/,那么a是controller的名字,action和id未给出,就按照default中的设定,action为Index,id为空。
说到这里,还没解释action是啥,id是啥呢。按照我们很久以前的url规划习惯,经常有article.aspx?action=modify&id=1这样子的url,controller的概念就是这里的acticle.aspx,表示文章处理的页面/模块;action就是url的query string中的action,一个处理模块通常由多个功能,action参数告诉它现在需要处理什么;id含有通常是跟随action而定的,比如刚才的例子,id表示要处理哪篇文章。
我们规划搜索引擎的URL吧:
路径 |
页面内容 |
/ |
首页 |
/keyword |
搜索结果页 |
于是我们可以将上面的路由代码改为:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute( name: "HomePage", url: "", defaults: new { controller = "Home", action = "Index" } );
routes.MapRoute( name: "SearchResult", url: "{keyword}", defaults: new { controller = "Home", action = "Search", keyword = UrlParameter.Optional } ); } } |
注意,我们使用了一个名为Home的controler,以及名为Index、Search的两个action,Search这个action带参数keyword。
建立相应的代码和页面
建立controller
右键单击Controllers目录,选择Add=>Controller:
输入名字HomeController,选择模版类型为Empty MVC controller:
注意,必须使用这个名字!因为我们之前在URL路由中设置的Controller名字是Home。
建好后得到一个比较简单的代码,将其改造成如下的样子:
public class HomeController : Controller { // // GET: /
public ActionResult Index() { return View(); }
// // GET: /keyword
public ActionResult Search(string keyword) { return View(); } } |
建立View
右键单击View目录,新建子目录Home(一定要与controller同名),然后右键单击Home目录,选择Add=>View:
名字改为Index,其余默认:
注意,必须为Index,与action保持同名。然后用同样方法建立一个Search页面。
到现在为止,我们的网站可以跑了!访问根目录:
访问搜索结果页面:
建立搜索结果
建立model
考虑到搜索结果中,controller处理后需要把结果传递给页面,我们建立一个model类来表示这个数据结构。右键单击Models目录,新建一个C#代码文件,定义如下类型:
namespace SearchEngine.Models { public class SearchResultItem { public string Title { get; set; } public string Link { get; set; } public string Description { get; set; } }
public class SearchResult { public string Keyword { get; set; } public List<SearchResultItem> Results { get; set; } } } |
改造controller
改写controller中的Search方法,改成:
public ActionResult Search(string keyword) { if (keyword == null) { return RedirectToAction("Index"); }
Models.SearchResult result = new Models.SearchResult(); result.Keyword = keyword; result.Results = new List<Models.SearchResultItem>();
for (int i = 0; i < 10; ++i) { Models.SearchResultItem item = new Models.SearchResultItem(); item.Link = "http://www.streamlet.org/"; item.Title = "溪流软件工作室"; item.Description = "快来访问溪流软件工作室!";
result.Results.Add(item); }
return View(result); } |
注意,我们建立了数据result,通过View(result)传递给页面。
改造View
改写Search.cshtml,改为:
@model SearchEngine.Models.SearchResult @{ ViewBag.Title = "Search"; }
<h2>Search</h2>
<p>搜索“@Model.Keyword”的结果:</p>
@foreach (var item in Model.Results) { <p> <a href="@item.Link" target="_blank">@item.Title</a><br /> @item.Description </p> } |
第一行@model声明本页面的数据模型。后面@开头的都是C#语句,剩余的是HTML。大多数情况下,Razor能识别@的结束,这比<% %>、<? ?>书写起来都简洁一点。
再访问一下搜索结果页:
美化及搜索框处理
这部分利用HTML的知识和PS功底即可。我这里简单的放了一个Logo和搜索框。首页代码如下:
@{ ViewBag.Title = "世界第一搜索引擎"; }
@Styles.Render("~/Content/Index.css")
@section scripts { <script type="text/javascript"> var search = function () { var keyword = $("#keyword").val(); window.location = "/" + keyword; } </script> }
<br /> <br /> <br /> <br /> <br /> <br /> <br />
<div id="searchBox"> <img src="~/Images/Logo.png" /><br /> <br /> <input id="keyword" type="text" /> <input id="submit" type="submit" value="搜索" onclick="javascript: search();" /> </div> |
页面效果如下:
搜索结果页面代码如下:
@model SearchEngine.Models.SearchResult @{ ViewBag.Title = "Search"; }
@Styles.Render("~/Content/Search.css")
@section scripts { <script type="text/javascript"> var search = function () { var keyword = $("#keyword").val(); window.location = "/" + keyword; } </script> }
<div id="searchBox"> <a href="/"><img src="~/Images/Logo.png" /></a><br /> <input id="keyword" type="text" value="@Model.Keyword" /> <input id="submit" type="submit" value="搜索" onclick="javascript: search();" /> </div>
<br />
@foreach (var item in Model.Results) { <p> <a href="@item.Link" target="_blank">@item.Title</a><br /> @item.Description </p> }
|
页面效果如下:
无搜索数据时的处理
目前,我们在Search方法中生成的数据是假的。实际情况中要根据实际结果来。当没有数据的时候,我们要给出友好提示。
因此,将controller中Search方法改为:
public ActionResult Search(string keyword) { if (keyword == null) { return RedirectToAction("Index"); }
Models.SearchResult result = new Models.SearchResult(); result.Keyword = keyword; result.Results = new List<Models.SearchResultItem>();
//for (int i = 0; i < 10; ++i) //{ // Models.SearchResultItem item = new Models.SearchResultItem(); // item.Link = "http://www.streamlet.org/"; // item.Title = "溪流软件工作室"; // item.Description = "快来访问溪流软件工作室!";
// result.Results.Add(item); //}
return View(result); } |
Search页面相应地改为:
@model SearchEngine.Models.SearchResult @{ ViewBag.Title = "Search"; }
@Styles.Render("~/Content/Search.css")
@section scripts { <script type="text/javascript"> var search = function () { var keyword = $("#keyword").val(); window.location = "/" + keyword; } </script> }
<div id="searchBox"> <a href="/"><img src="~/Images/Logo.png" /></a><br /> <input id="keyword" type="text" value="@Model.Keyword" /> <input id="submit" type="submit" value="搜索" onclick="javascript: search();" /> </div>
<br />
@if (Model.Results == null || Model.Results.Count() == 0) { <strong>根据相关法律法规和政策,部分搜索结果未予显示。</strong> } else { foreach (var item in Model.Results) { <p> <a href="@item.Link" target="_blank">@item.Title</a><br /> @item.Description </p> } } |
页面效果:
例子代码:http://pan.baidu.com/s/1o6r5Qga(SearchEngine.rar)
演示页面:http://www.streamlet.org/Search/