近来学习了一下Nancy这个框架,感觉挺好用的,就写篇简单的文章记录一下大致用法,由于是刚接触,写的代码

可能不规范,也没有具体的分层。。莫吐槽。。。

Nancy的官网:http://nancyfx.org/

GitHub地址:https://github.com/NancyFx/Nancy

Nancy在文档的介绍 -- 轻量级

" Nancy is a lightweight, low-ceremony, framework for building HTTP based services on .NET and Mono. "

Nancy有多种Hosting:

  Hosting Nancy with ASP.NET
  Hosting Nancy with WCF
  Hosting Nancy with Azure
  Hosting Nancy with OWIN
            Web (Katana)
            Self Hosting
            Environment Variables
            Conditional Pass-through
  Hosting Nancy with Umbraco
  Hosting Nancy with Nginx on Ubuntu
  Hosting Nancy with FastCgi
  Self Hosting Nancy
  Implementing a Host
  Accessing the client certificate when using SSL

本文写的Demo是基于Hosting Nancy with ASP.NET。

开发环境 :win 10 + VS2015 Community + SqlServer 2012

废话不多说,开始正题:

一、新建一个空的asp.net项目

建好之后空空如也

二、通过NuGet添加Nancy相关的Packages

    

主要是Nancy、Nancy.Hosting.Aspnet、Nancy.Viewengines.Razor这三个。其版本依次为1.4.3、1.4.1、1.4.3

我这里选择的视图引擎是Razor,习惯了,你们可以选择其他,这个问题不大。

三、更新一下Razor

默认安装Nancy.Viewengines.Razor之后,它会安装 Microsoft.AspNet.Razor.2.0.30506.0,我们用的是最新版的3.2.3

在NuGet通过已安装的Packages来更新

到这里,基本的工作已经OK了。

这里用到了Dapper,所以我也添加了它的引用。

注:这里是通过NuGet安装的,所以它也在web.config里生成了一些配置。如果是手动添加引用的,注意要修改web.config。

四、加入css和javascript文件

允许我偷偷懒,这里的我是直接copy一般新建mvc项目的,就连页面布局也是用的新建项目的模板。。

五、新建Modules、Models、ViewModels、Views四个文件夹

Modules-->相当于MVC中的Controllers文件夹

Models-->存放模型

ViewModels-->存放视图模型

Views-->存放视图

六、在Views下面建一个布局页_Layout.cshtml

就新建的mvc模板copy过来,稍加修改

 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - Catcher's NancyDemo</title>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/Site.css" rel="stylesheet" />
<link rel="shortcut icon" href="~/favicon.ico" />
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/" class="navbar-brand">NancyDemo</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">首页</a></li>
<li><a href="/home/about">关于我们</a></li>
<li><a href="/home/contact">联系我们</a></li>
<li><a href="/movie/">电影</a></li>
<li><a href="/movie-type/">类型</a></li>
</ul>
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>

_Layout.cshtml

七、在Modules下新建一个HomeModules.cs,具体如下:

 public class HomeModule : NancyModule
{
public HomeModule()
{
Get["/"] = _ => ShowHomePage();
Get["/home/about"] = _ => ShowAboutPage();
Get["/home/contact"] = _ => ShowContactPage();
} private dynamic ShowContactPage()
{
return View["Contact"];
} private dynamic ShowAboutPage()
{
return View["About"];
} private dynamic ShowHomePage()
{
return View["Index"];
}
}

HomeModule.cs

同时在Views文件夹下面新建一个Home的子文件夹,在把mvc模板中的Index.cshtml,About.cshtml,Contact.cshtml

copy过来稍加修改

 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
@{
ViewBag.Title = "首页";
Layout = "Views/_Layout.cshtml";
}
<div class="jumbotron">
<h1>NancyDemo</h1>
<p class="lead">Nancy is a lightweight, low-ceremony, framework for building HTTP based services on .NET and Mono. The goal of the framework is to stay out of the way as much as possible and provide a super-duper-happy-path to all interactions.</p>
<p><a href="http://nancyfx.org/" class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
</div>

Index.cshtml

 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
@{
ViewBag.Title = "About";
Layout = "Views/_Layout.cshtml";
}
<h2>@ViewBag.Title.</h2>
<p>Use this area to provide additional information.</p>

About.cshtml

 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
@{
ViewBag.Title = "Contact";
Layout = "Views/_Layout.cshtml";
}
<h2>@ViewBag.Title.</h2> <address>
One Microsoft Way<br />
Redmond, WA -<br />
<abbr title="Phone">P:</abbr>
425.555.
</address> <address>
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

Contact.cshtml

然后F5,跑一下

     

已经能跑起来了。

下面就是结合数据库了。

八、在MOdels下面建两个类

 public class Movie
{
public int MovieId { get; set; } public string MovieName { get; set; } public int MovieTypeId { get; set; } public DateTime MovieAddTime { get; set; } public virtual MovieType MovieType { get; set; } public Movie()
{
MovieAddTime = DateTime.Now;
}
}

Movie.cs

     public class MovieType
{
public MovieType()
{
Movies = new HashSet<Movie>();
} public int TypeId { get; set; } public string TypeName { get; set; } public virtual ICollection<Movie> Movies { get; set; }
}

MovieType.cs

以Movie表为例,继续下面的工作。

九、由于movie表中的数据展示,抽取出应该有的视图模型

在ViewModels文件夹下面建立MovieViewModel.cs、MovieListViewModel.cs

     public class MovieViewModel
{
public int MovieId { get; set; } public string MovieName { get; set; } public int MovieTypeId { get; set; } public string MovieTypeName { get; set; } public DateTime MovieAddTime { get; set; }
}

MovieViewModel.cs

      public class MovieListViewModel
{
public IEnumerable<MovieViewModel> Movies { get; set; }
}

MovieListViewModel.cs

十、现在就该写写烦人mvc控制器的东西了,在Modules文件夹下建立MovieModules.cs

把数据提取出来

     public class MovieModule : NancyModule
{
public MovieModule() : base("/movie")
{
Get["/"] = _ => ShowMovieIndexPage();
} private readonly string _sqlconnection =
"Data Source=192.168.0.71;Initial Catalog=NancyDemo;User Id=sa;Password=dream_time1314;"; public SqlConnection OpenConnection()
{
SqlConnection connection = new SqlConnection(_sqlconnection);
connection.Open();
return connection;
} private dynamic ShowMovieIndexPage()
{
using (IDbConnection conn = OpenConnection())
{
string getAllMoviesStoredProcedure = @"up_GetAllMovies";
MovieListViewModel viewModel = new MovieListViewModel
{
Movies = conn.Query<MovieViewModel>(getAllMoviesStoredProcedure,
null, null, true, null, CommandType.StoredProcedure)
};
return View["Index", viewModel];
}
}
}

MovieModule.cs

然后就去编写视图

在Views下面新建一个Movie文件夹,用于存放相关视图

 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<MovieDemo.ViewModels.MovieListViewModel>
@{
Layout = "Views/_Layout.cshtml";
ViewBag.Title = "电影列表";
}
<a href="/movie/create">添加</a>
<div>
<table class="table">
<tr>
<th>
#
</th>
<th>
电影名称
</th>
<th>
电影类型
</th>
<th>
添加时间
</th>
<th></th>
</tr>
@foreach (var item in Model.Movies)
{
<tr>
<td>
@item.MovieId
</td>
<td>
@item.MovieName
</td>
<td>
@item.MovieTypeName
</td>
<td>
@item.MovieAddTime
</td>
<td>
<a href="/movie/edit/@item.MovieId">修改</a>
<a class="delete" href="/movie/delete/@item.MovieId">删除</a>
</td>
</tr>
}
</table>
</div>

Index.cshtml

F5跑一下

OK!

然后就是修改某条数据,

在MovieModule.cs 的构造函数中添加

            Get["/edit/{movieId}"] = _ => ShowMovieEditPage(_.movieId);
Post["/edit/{movieId}"] = _ =>
{
var movie = this.Bind<Movie>();
return MovieEdit(movie);
};

然后添加ShowMovieEditPage和MovieEdit这两个方法

 private dynamic ShowMovieEditPage(int movieId)
{
string getOneMovieStoredProcedure = @"up_GetMovieByMovieId";
string getALLTypeStoredProcedure = @"up_GetAllMovieTypes";
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@MovieId", movieId); using (IDbConnection conn = OpenConnection())
{
var movieViewModel = conn.Query<MovieViewModel>(getOneMovieStoredProcedure, dynamicParameters, null, true, null, CommandType.StoredProcedure).SingleOrDefault();
ViewBag.typeList = conn.Query<MovieType>(getALLTypeStoredProcedure, null, null, true, null, CommandType.StoredProcedure);
return View["Edit", movieViewModel]; }
}

ShowMovieEditPage

     private dynamic MovieEdit(Movie movie)
{
string updateMovieStoredProcedure = @"up_UpdateMovieByMovieId";
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@MovieId", movie.MovieId);
dynamicParameters.Add("@MovieName", movie.MovieName);
dynamicParameters.Add("@MovieTypeId", movie.MovieTypeId); using (IDbConnection conn = OpenConnection())
{
conn.Execute(updateMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure);
return Response.AsRedirect("/movie");
}
}

MovieEdit

添加一个Movie的Edit视图

 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<MovieDemo.ViewModels.MovieViewModel>
@{
ViewBag.Title = "修改电影信息";
Layout = "Views/_Layout.cshtml";
} <form action="/movie/edit/@Model.MovieId" method="post">
<div class="form-group">
<label class="control-label col-md-2">电影名称</label>
<div class="col-md-10">
<input type="text" class="form-control" id="MovieName" name="MovieName" value="@Model.MovieName" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">电影类型</label>
<div class="col-md-10">
<select id="MovieTypeId" name="MovieTypeId" class="form-control">
@foreach (var item in (System.Collections.Generic.List<MovieDemo.Models.MovieType>)ViewBag.typeList)
{
if (Model.MovieTypeId == item.TypeId)
{
<option selected="selected" value="@item.TypeId">@item.TypeName</option>
}
else
{
<option value="@item.TypeId">@item.TypeName</option>
}
} </select>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="修改" class="btn btn-default" />
</div>
</div>
</form>

Edit.cshtml

然后即可跑起来了。

其中用到了一个模型绑定,需要添加Nancy.ModelBinding引用,有了这个绑定,省了很多事!!

 var movie = this.Bind<Movie>();

同理,增加和删除也是同样的做法,下面是MovieModule.cs的完整代码

 using MovieDemo.Models;
using MovieDemo.ViewModels;
using Dapper;
using Nancy;
using Nancy.ModelBinding;
using System.Data;
using System.Data.SqlClient;
using System.Linq; namespace MovieDemo.Modules
{
public class MovieModule : NancyModule
{
public MovieModule() : base("/movie")
{
Get["/"] = _ => ShowMovieIndexPage(); Get["/edit/{movieId}"] = _ => ShowMovieEditPage(_.movieId);
Post["/edit/{movieId}"] = _ =>
{
var movie = this.Bind<Movie>();
return MovieEdit(movie);
}; Get["/create"] = _ => ShowMovieCreatePage();
Post["/create"] = _ =>
{
var movie = this.Bind<Movie>();
return MovieCreate(movie);
}; Get["/delete/{movieId}"] = _ => MovieDelete(_.movieId);
} private readonly string _sqlconnection =
"Data Source=192.168.0.71;Initial Catalog=NancyDemo;User Id=sa;Password=dream_time1314;"; public SqlConnection OpenConnection()
{
SqlConnection connection = new SqlConnection(_sqlconnection);
connection.Open();
return connection;
} private dynamic MovieDelete(int movieId)
{
string deleteMovieStoredProcedure = @"up_DeleteMovieByMovieId";
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@MovieId", movieId);
using (IDbConnection conn = OpenConnection())
{
conn.Execute(deleteMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure);
return Response.AsRedirect("/movie");
}
} private dynamic MovieCreate(Movie movie)
{
string createMovieStoredProcedure = @"up_InsertMovie";
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@MovieName", movie.MovieName);
dynamicParameters.Add("@MovieTypeId", movie.MovieTypeId);
dynamicParameters.Add("@MovieAddTime", movie.MovieAddTime); using (IDbConnection conn = OpenConnection())
{
conn.Execute(createMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure);
return Response.AsRedirect("/movie");
}
} private dynamic ShowMovieCreatePage()
{
string getALLTypeStoredProcedure = @"up_GetAllMovieTypes";
using (IDbConnection conn = OpenConnection())
{
MovieTypeListViewModel viewModel = new MovieTypeListViewModel
{
MovieTypes = conn.Query<MovieType>(getALLTypeStoredProcedure, null, null, true, null, CommandType.StoredProcedure)
};
return View["Create", viewModel];
}
} private dynamic MovieEdit(Movie movie)
{
string updateMovieStoredProcedure = @"up_UpdateMovieByMovieId";
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@MovieId", movie.MovieId);
dynamicParameters.Add("@MovieName", movie.MovieName);
dynamicParameters.Add("@MovieTypeId", movie.MovieTypeId); using (IDbConnection conn = OpenConnection())
{
conn.Execute(updateMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure);
return Response.AsRedirect("/movie");
}
} private dynamic ShowMovieEditPage(int movieId)
{
string getOneMovieStoredProcedure = @"up_GetMovieByMovieId";
string getALLTypeStoredProcedure = @"up_GetAllMovieTypes";
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@MovieId", movieId); using (IDbConnection conn = OpenConnection())
{
var movieViewModel = conn.Query<MovieViewModel>(getOneMovieStoredProcedure, dynamicParameters, null, true, null, CommandType.StoredProcedure).SingleOrDefault();
ViewBag.typeList = conn.Query<MovieType>(getALLTypeStoredProcedure, null, null, true, null, CommandType.StoredProcedure);
return View["Edit", movieViewModel]; }
} private dynamic ShowMovieIndexPage()
{
using (IDbConnection conn = OpenConnection())
{
string getAllMoviesStoredProcedure = @"up_GetAllMovies";
MovieListViewModel viewModel = new MovieListViewModel
{
Movies = conn.Query<MovieViewModel>(getAllMoviesStoredProcedure,
null, null, true, null, CommandType.StoredProcedure)
};
return View["Index", viewModel];
}
}
}
}

MovieModule.cs

在添加相应的视图即可完成。

十一、下面还需要简单讲一下Bootstrapper

这个东西可以简单认为是引导程序,你可以自定义一些鬼东西,把它当作一个容器来用,

还可以通过它来实现依赖注入,里面有个默认的 TinyIoC 。。。。。详细介绍请看https://github.com/NancyFx/Nancy/wiki/Bootstrapper

下面是我的CustomBootstrapper.cs配置

 public class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
//container.Register<IMoviesRepository, MoviesRepository>();
//container.Register<IMovieTypesRepository, MovieTypesRepository>();
}
protected override void ConfigureConventions(NancyConventions nancyConventions)
{
base.ConfigureConventions(nancyConventions);
nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Scripts"));
nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Content"));
}
}

CustomBootstrapper.cs

依赖注入我是没有用的,所以我注释了。用过Ninject、Autofac、Unity这些的,相信依赖注入这一块理解起来So easy!

Nancy也有相应的拓展

https://github.com/NancyFx/Nancy.Bootstrappers.Autofac

https://github.com/NancyFx/Nancy.Bootstrappers.Unity

https://github.com/NancyFx/Nancy.Bootstrappers.Ninject

还有一块是ConfigureConventions

这一块似乎是用来处理静态文件(css、js)的,跟Mvc中的BundleConfig很类似

到这里,开发工作已经完成。下面是部署的时候了。

十二、将做的MovieDemo部署到Linux下

Linux环境及配置

CentOS 6.7  + mono 4.2.2 + Jexus 5.8.0

将发布过后的目录上传到centos的 /var/www/ 中,然后在 /usr/jexus/siteconf 中建立一个新的配置文件

重新启动jexus即可。

下面上几张运行的图

完全的无缝兼容。

本篇的完整代码已托管到GitHub

下载地址:https://github.com/hwqdt/Demos/tree/master/NancyDemoWithHostingAspnet

下雨天,心情压抑,写篇bolg来拯救自己的好心情 ~_~

后面有时间会写基于owin的demo

Nancy之基于Nancy.Hosting.Aspnet的小Demo的更多相关文章

  1. Nancy之基于Self Hosting的补充小Demo

    前面把Hosting Nancy with ASP.NET.Self Hosting Nancy和Hosting Nancy with OWIN 以demo的形式简单描述了一下. 这篇是为Self H ...

  2. Nancy之基于Nancy.Hosting.Self的小Demo

    继昨天的Nancy之基于Nancy.Hosting.Aspnet的小Demo后, 今天来做个基于Nancy.Hosting.Self的小Demo. 关于Self Hosting Nancy,官方文档的 ...

  3. Nancy之基于Nancy.Owin的小Demo

    前面做了基于Nancy.Hosting.Aspnet和Nancy.Hosting.Self的小Demo 今天我们来做个基于Nancy.Owin的小Demo 开始之前我们来说说什么是Owin和Katan ...

  4. 一个基于ES6+webpack的vue小demo

    上一篇文章<一个基于ES5的vue小demo>我们讲了如何用ES5,vue-router做一个小demo,接下来我们来把它变成基于ES6+webpack的demo. 一.环境搭建及代码转换 ...

  5. 一个基于ES5的vue小demo

    由于现在很多vue项目都是基于ES6开发的,而我学vue的时候大多是看vue官网的API,是基于ES5的,所以对于刚接触项目的我来说要转变为项目的模块化写法确实有些挑战.因此,我打算先做一个基于ES5 ...

  6. 基于FPGA的飞机的小游戏

    基于FPGA的飞机的小游戏 实验原理 该实验主要分为4个模块,采用至上而下的设计方法进行设计.由50M的晶振电路提供时钟源,VGA显示控制模块.图形显示控制模块.移动模块的时钟为25M,由时钟分频电路 ...

  7. 基于HTML5实现五彩连珠小游戏

    今天给大家分享一款基于HTML5实现五彩连珠小游戏.这款游戏的规则:点击彩球移动到期望的位置,每移动一次,画面将随机出现3个新的彩球:当同一颜色的彩球连成5个一行或一列或一斜线时,这5个彩球同时消失, ...

  8. 基于Shiro,JWT实现微信小程序登录完整例子

    小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html ...

  9. CRMEB系统就是集客户关系管理+营销电商系统,能够真正帮助企业基于微信公众号、小程序实现会员管理、数据分析,精准营销的电子商务管理系统。可满足企业新零售、批发、分销、等各种业务需求。

    **可以快速二次开发的开源小程序商城系统源码**源码开源地址:https://github.crmeb.net/u/LXT 项目介绍: CRMEB系统就是集客户关系管理+营销电商系统,能够真正帮助企业 ...

随机推荐

  1. VS快捷键总结(包括ReSharper)

    Shift+Alt+Enter 全屏显示代码Ctrl+E 最近文件列表Ctrl+B 转到定义Ctrl+Alt+B 转到继承类或接口处Ctrl+U 转到基类Ctrl+D 复制当前行或选定的块(Dupli ...

  2. SQL Server复制出错文章集锦

    SQL Server复制出错文章集锦 为了方便大家对数据库复制过程中出错的时候更好地解决问题 本人收集了SQL Server相关复制出错解决的文章   The process could not ex ...

  3. CYQ.Data+EasyUI开发:几个相关的问题CheckBox、Tree、TreeGrid

    前言: 话说到新的公司已经呆了三个星期了,从上班的第二天开始就一直在写项目文档和给开发人员培训,以至于我的QQ签名从"我不是来搞培训的“到最后直接换成”我是来搞培训的“. 虽然挂名开发经理, ...

  4. Redhat环境下编译安装Google Bazel

    Redhat环境下编译安装bazel 作者:Jack47 目前Google Bazel没有提供各个操作系统下的二进制安装包,只提供源代码,需要我们自己编译安装,详情可以见我翻译的中文版Google B ...

  5. 如何在没有域的环境中搭建AlwaysOn(二)

    对DBA而言,不需要域就可以搭建SQL Server AlwaysOn是Windows Server 2016中最令人兴奋的功能了,它不仅可以降低搭建的成本,而且还减少了部署和运维的工作量. 上篇博客 ...

  6. ASP.NET Web API自身对CORS的支持: EnableCorsAttribute特性背后的故事

    从编程的角度来讲,ASP.NET Web API针对CORS的实现仅仅涉及到HttpConfiguration的扩展方法EnableCors和EnableCorsAttribute特性.但是整个COR ...

  7. Cocos2d-x 3.x游戏开发之旅

    Cocos2d-x 3.x游戏开发之旅 钟迪龙 著   ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...

  8. C语言-结构体struct-联合体union-枚举enum

    结构体 在Java中,我们要表示一个复合的数据类型就会使用对象去封装.而C就有结构体. 结构体是C语言中自定义的数据类型,是一组变量的集合,有别于数组,数组仅限于同一种数据类型,而结构体可以是任何数据 ...

  9. sql复习第四次

    1.关系操作的特点是集合操作 2.关系模型的完整性规则包括实体完整性规则,参照完整性规则,用户定义的完整性规则 3.rou联接运算是由笛卡儿积和选择操作组合而成的 4.自然联接运算是由笛卡儿积,选择, ...

  10. Angularjs在控制器(controller.js)的js代码中使用过滤器($filter)格式化日期/时间实例

    Angularjs内置的过滤器(filter)为我们的数据信息格式化提供了比较强大的功能,比如:格式化时间,日期.格式化数字精度.语言本地化.格式化货币等等.但这些过滤器一般都是在VIEW中使用的,比 ...