这篇文章主要介绍 ASP.NET 5 中新建项目的结构和之前的差异, 介绍的比较细致, 需要的朋友可以参考下
初识项目
打开 VS2015, 创建 web 项目, 选择 ASP.NET Web Application, 在弹出的窗口里选择 ASP.NET 5 Website 模板创建项目, 图示如下:
我们可以看到, 此时 Web Forms\MVC\Web API 复选框都选择不了, 原有是因为在 ASP.NET 5 中做了大量更改, 移除了 Web Forms 功能, 将 MVCWeb APIWeb Pages 这些功能合在了一起, 所以自然就不需要这些复选框了另外由于是 CTP 版, 所以目前还没有提供单元测试项目的创建
新创建的项目在 VS 的解决方案目录结构和实际文件夹的目录结构分别如下:
注意: 上图是在 VS 预览版中的截图, 在新版的 RC 版本中, 默认的客户端构建工具变成了 gulp(即配置文件是 gulpfile.js), 而非原来的 grunt 了
两个图的差异非常大, 我们来一一分析一下这些差异
项目结构差异
通过图示, 我们可以看到, 在根目录中, 不仅项目文件从从 csproj 变成了 xproj, 还少了很多之前的文件(如 web.config), 但也多了很多不同的文件以及文件夹, 我们先列出这些不同的文件盒文件夹, 再来一一讲解这些内容
文件 / 文件夹 | 功能描述 |
---|---|
config.json | 程序的配置文件,类似于 web.config。 |
project.json | 该项目的主要配置,主要是负责程序集、项目部署等方面。部分功能类似于之前的 package.config。 |
package.json | npm 的配置文件,npm 是基于 Nodejs 的包管理器。 |
bower.json | Bower 管理器的配置文件,Bower 是专门用于管理前端项目的包管理器。 |
gulpfile.js | 是 gulp 的配置文件,gulp 是基于 Nodejs 的 Javascript 任务管理器,在 ASP.NET5 中主要是用于管理 NPM 和 Bower 中的内容。 |
Stratup.cs | 程序启动入口,类似于 Global.asax。 |
Project_Readme.html | 项目说明文件,没啥用。 |
wwwroot | 静态资源文件(如 CSS、图片、js)的存放目录。 |
Dependencies | Bower 和 NPM 的依赖管理包。 |
References | 程序集引用,和以前的类似,但现在有了版本之分(如 ASP.NET 5.0 和 ASP.NET Core 5.0)。 |
project.json
project.json 是项目的核心配置文件, 示例如下:
- {
- "webroot": "wwwroot",
- "version": "1.0.0-*",
- "dependencies": {
- "Microsoft.AspNet.Diagnostics": "1.0.0-beta4",
- "Microsoft.AspNet.Mvc": "6.0.0-beta4",
- "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta4",
- "Microsoft.AspNet.Server.IIS": "1.0.0-beta4",
- "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
- "Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
- "Microsoft.AspNet.Tooling.Razor": "1.0.0-beta4",
- "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4",
- "Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta4",
- "Microsoft.Framework.Logging": "1.0.0-beta4",
- "Microsoft.Framework.Logging.Console": "1.0.0-beta4",
- "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta4",
- "Microsoft.Framework.ConfigurationModel.UserSecrets": "1.0.0-beta4"
- },
- "commands": {
- "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000",
- "gen": "Microsoft.Framework.CodeGeneration"
- },
- "frameworks": {
- "dnx451": { },
- "dnxcore50": { }
- },
- "exclude": [
- "wwwroot",
- "node_modules",
- "bower_components"
- ],
- "publishExclude": [
- "node_modules",
- "bower_components",
- "**.xproj",
- "**.user",
- "**.vspscc"
- ],
- "scripts": {
- "postrestore": [ "npm install", "bower install" ],
- "prepare": [ "gulp copy" ]
- }
- }
由于该文件的详细参数非常多, 具体详细内容请参考 http://go.microsoft.com/fwlink/?LinkID=517074 , 在这里我们主要讲解如下 3 个类型的内容
webroot
webroot 是指定该 web 项目的静态文件存放地址, 目前是用于在发布的时候讲该目录中的内部发布的正确的位置 (详细内容可以在部署发布章节中找到) 注意 BookStore 解决方案中带有地球图标的 wwwroot 目录是真实的文件夹路径, 我们可以对其进行修改, 比如将其修改为 wwwroot1, 那么相应的 webroot 的值也应该修改为 wwwroot1, 因为 gulpfile.js 里代码要通过 project.webroot 来使用该目录, 以便能够将 bower 管理的前端库都复制到正确的目录
程序集管理
在解决方案的 References 节点点, 我们看到有两个分类, 分别是: DNX 4.5.1 和 DNX Core 5.0, 其中 DNX Core 5.0 就是我们所说的云优化版(即可以在其它操作系统下部署的跨平台版), 而 DNX 4.5.1 则是和之前版本一样的全功能版, 这两个版本的程序集是通过 dependencies 节点进行管理的
在一级 dependencies 节点, 主要是定义该项目的通用程序集引用以及版本, 而不同的版本的程序集则在 framworks 下的各版本下的 dependencies 节点进行维护, 比如:
- "frameworks": {
- "dnx451": {
- "dependencies": {"log4net": "2.0.3"} /* 只在全功能版中引入 log4net 程序集 */
- },
- "dnxcore50": { }
- }
上述两种类型的程序集在维护的时候, 都有智能提示(包括程序集名称以及版本号), 在当定义完自己要用的程序集并保持之后, 系统会自动从 Nuget 上下载所需要的程序集, 你也可以通过右键 References 选择 Restore Packages 来更新所有的程序集引用同时你依然可以通过右键 References 的形式通过 Nuget 来管理这些程序集
脚本事件
新版的 VS2015 允许我们在 build 解决方案之前之后过程; 下载程序集之前之后; 更新程序集之前之后自定义一些基于 Nodejs 的自定义事件来执行该事件在 project.json 中的定义节点是 scripts, 示例如下:
- "scripts": {
- "postrestore": [ "npm install" ], // 在更新所有的程序集之前执行 npm install 事件
- "prepare": [ "gulp copy" ]
- // 在打开解决方案之前, 执行 gulp 任务, 调用 bower 的 install 方法
- }
具体的事件名称如下:
时机 | 描述 |
---|---|
prebuild | 构建之前执行 |
postbuild | 构建之后执行 |
prepack | packing 之前执行 |
postpack | packing 之后执行 |
prerestore | restoring packages 之前执行 |
postrestore | restoring packages 之后执行 |
package.json
package.json 是 NPM 管理器的配置文件, 由于在 VS2015 默认就深度集成了 Nodejs, 而 NPM 又是 Nodejs 的默认包管理器, 所以所有基于 Nodejs 的包都要在这里进行配置该配置文件的默认配置如下:
- {
- "name": "ASP.NET",
- "version": "0.0.0",
- "devDependencies": {
- "gulp": "3.8.11",//gulp 任务管理器
- "rimraf": "2.2.8" // 递归删除文件的 nodejs 包
- }
- }
上述代码中的 rimraf 是一个递归删除文件的 nodejs 包, 我们也可以引用其他插件, 像 project.json 文件中管理程序集一样, 在 package.json 文件中来管理前端程序的各种包, 例如 jquery,bootstrap 等等, 比如我们要安装一个 express 包, 只需要在 json 文件中添加一个 express 字符串键, 并选择器版本就可以了, 系统会自动下载该 NPM 包并显示在解决方案的 Dependencies->NPM 节点下
注意: 已经安装的包不能自动移除(即不能通过在 JSON 中移除配置), 需要右键执行该包, 并手工卸载
bower.json
所有的前端包都配置子 bower.json 文件中, 比如你需要的 jquerybootstrapangular 等等, 其管理方式与 project.json 里的程序集和 package.json 里的 npm 包一样, 都是通过在 dependencies 节点下声明包的名称和版本来实现的
我们可以在此声明一个 angular 包, 保存以后就可以看到在解决方案 Dependencie->Bower 节点下该 angular 已经自动下载好了, 编译项目, 就可以看到在 wwroot/lib 也可以看到 angular 文件夹以及相应的文件了
在 bower.json 还有一个 exportsOverride 节点非常重要, 他扩展了原来 bower 的前端文件 copy 机制, 默认情况下 bower 只会复制 main 节点定义的文件但有时候我们要复制的文件可能不止这些, 所以 grunt-bower-task 插件就扩展了该功能, 定义了这个 exportsOverride 节点, 其使用规则如下:
如果 Bower 包定义了 main 文件节点, 就将其复制到 wwwroot/lib 下如果 Bower 包定义了的 main 节点为空, 则将整个包的目录都复制到 wwwroot/lib 下如果定义了 exportsOverride 节点, 则只会把该节点指定的文件复制到 wwwroot/lib 下
注意, exportsOverride 节点中定义的 key/value, 其中 key 表示要文件复制目标 (即 wwwroot/lib 下) 对应包名下的子目录, value 表示源文件目录或文件例如:
- "bootstrap": {
- "js": "dist/js/*.*", // 将 dist/js / 下的所有文件, 复制到 wwwroot/lib/bootstrap/js 目录下
- "css": "dist/css/*.*",
- "fonts": "dist/fonts/*.*"
- },
- "jquery": {
- "":"jquery.{js,min.js,min.map}" // 将 jquery.js,jquery.min.js,jquery.min.map 文件复制到 wwwroot/lib/jquery 目录下
- },
注意: 和 NPM 类似, bower.json 里的配置的包, 不能自动移除, 需要从 Bower 里卸载该包, 并将相关的文件从 wwwroot/lib 中移除
gulpfile.js
gulpfile.js 是 gulp 任务管理器的配置文件, 默认情况下, 该配置文件会把 wwwroot/lib 目录下的所有文件清除(clean 任务), 然后重新从 bower_components 目录中复制一份(copy 任务)
该文件配置的修改会影响到 VS 里的
Task Runner Explorer
的显示, 如下图所示:
以默认配置为例, 该配置文件在 Task 目录下注册了两个任务, 分别是 clean 和 copy, 并默认在 VS 解决方案清除编译以后重新执行 clean 任务, 但我们也可以给该任务绑定任何一个执行时间点, 我们可以右键该任务 ->绑定 ->在构建之前, 然后在点击面板左边的刷新按钮, 这时候该绑定内容就会同步保存在 gulpfile.js 的第一行, 代码如下:
/// <binding BeforeBuild='copy' Clean='clean' />
此时, 删除 wwwroot/lib 目录下所有的文件, 然后重新编译 BookStore 项目, 就会自动在 wwwroot/lib 目录下生成所有需要的文件, 即将 Bower.json 里定义的各种包按照配置需求复制到该目录下
clean 任务
clean 任务的主要作用, 是在编译之前或清理解决方案是, 将 lib 目录下的前端文件全部删除, 以便重新 copy 新的文件具体分析如下:
- var gulp = require("gulp"), // 引用 gulp
- rimraf = require("rimraf"),// 引用 rimraf
- fs = require("fs"); // 引用文件系统
- eval("var project =" + fs.readFileSync("./project.json")); // 读取 project.json 配置文件
- var paths = {
- bower: "./bower_components/",
- lib: "./" + project.webroot + "/lib/"
- };
- gulp.task("clean", function (cb) { // 注册 clean 任务
- rimraf(paths.lib, cb); // 递归删除 lib 目录下的所有文件
- });
copy 任务
copy 任务则很简单, 将 bower_components 目录下符合条件的文件复制 lib 目录下, 分析如下:
- gulp.task("copy", ["clean"],
- function() { // 注册 copy 任务
- var bower = { // 目录对应关系
- "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
- "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
- "hammer.js": "hammer.js/hammer*.{js,map}",
- "jquery": "jquery/jquery*.{js,map}",
- "jquery-validation": "jquery-validation/jquery.validate.js",
- "jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
- }
- for (var destinationDir in bower) {
- gulp.src(paths.bower + bower[destinationDir]) // 读取源目录
- .pipe(gulp.dest(paths.lib + destinationDir)); // 复制到目标文件夹
- }
- });
Grunt 任务
VS2015 中, 虽然默认支持的是 Gulp 构建工具, 但其实还支持 Grunt 构建工具, 使用方式和 Gulp 类似, 要使用 Grunt, 也需要引用类似的依赖包, 示例如下:
- {
- "version": "0.0.0",
- "name": "","devDependencies": {"grunt":"0.4.5", //grunt 任务管理器"grunt-bower-task":"0.4.0" // 基于 grunt 的 bower 管理插件
- }
- }
上述代码中的 grunt-bower-task 是一个基于 grunt 的 bower 管理插件, 用于自动执行 bower 的 install 命令来安装 Bower 包
注意: 已经安装的包不能自动移除(即不能通过在 JSON 中移除配置), 需要右键执行该包, 并手工卸载
gruntfile.js 是 grunt 任务管理器的配置文件, 要使用 grunt, 就需要创建 gruntfile.js 文件, 默认情况下, 该配置文件只配置了 grunt-bower-task 插件的任务执行, 该插件会读取 bower.json 配置信息, 将相关的包通过 bower:install 命令都安装到指定的目录下(默认是通过 targetDir 设置的 wwwroot/lib 目录
该文件配置的修改会影响到 VS 里的
Task Runner Explorer
的显示, 如下图所示:
以默认配置为例, 该配置文件注册了一个名为 default 的任务在该面板里 (Alias Tasks 列表中) 显示, 该任务也是 Grunt 的默认任务名称, 但并没有定义该任务在什么时候执行, 所以这时候我们可以给该任务绑定一个执行时间点, 我们可以右键该任务 ->绑定 ->在构建之前, 然后在点击面板左边的刷新按钮, 这时候该绑定内容就会同步保存在 gruntfile.js 的第一行, 代码如下:
/// <binding BeforeBuild='default' />
此时, 删除 wwwroot/lib 目录下所有的文件, 然后重新编译 BookStore 项目, 就会自动在 wwwroot/lib 目录下生成所有需要的文件, 即将 Bower.json 里定义的各种包按照配置需求复制到该目下而 Tasks 中的任务, 则是从 grunt.loadNpmTasks 里加载的包里分析出来的任务, 如 bower
我们再来练习一个例子, 假设我们编译之前要对 wwwroot/css / 目录下的 site.css 文件进行压缩(压缩成 site.min.css), 我们则可以安装如下方式进行操作:
首先, 在 package.json 里定义一个可以压缩 CSS 代码的 grunt 插件:
- {
- "version": "0.0.0",
- "name": "","devDependencies": {"grunt":"0.4.5","grunt-bower-task":"0.4.0","grunt-contrib-cssmin":"0.12.2" /* 新的插件 */
- }
- }
然后在 grunt.initConfig 下的 bower 同级节点下面, 添加如下内容:
- /* 压缩 css*/
- cssmin: {
- target: {
- options: {
- sourceMap: true,
- },
- files: {
- /* 输出文件路径: 原始文件路径 */
- 'wwwroot/css/site.min.css': 'wwwroot/css/site.css'
- }
- }
- }
最后再注册此插件, 代码如下:
- grunt.loadNpmTasks('grunt-contrib-cssmin'); /* 压缩 css*/
这样, 你就可以在 Task Runner Explorer 面板中看到 cssmin 任务, 然后运行它了, 当然你也可以将该任务和 default 任务一起添加到编译构建之前进行执行代码如下:
/// <binding BeforeBuild='default, cssmin' />
另外, 在给一些例子, 一个是用于 js 压缩, 一个是用于 less 编译, 代码如下:
- /*package.json*/
- "grunt-contrib-uglify": "0.9.1",
- "grunt-contrib-less": "1.0.1"
- /*gruntfile.js*$* 压缩 js*/
- uglify: {
- target: {
- options: {
- sourceMap: true,
- },
- files: {
- 'wwwroot/Scripts/site.min.js': 'wwwroot/Scripts/site.js'
- }
- }
- },
- /* 编译 less*/
- less: {
- // 开发版(无压缩)
- development: {
- options: {
- sourceMap: true
- },
- files: {
- 'wwwroot/Styles/site.css': 'wwwroot/Lesses/site.less'
- }
- },
- // 生产版(压缩)
- production: {
- options: {
- compress: true
- },
- files: {
- 'wwwroot/Styles/site.min.css': 'wwwroot/Lesses/site.less'
- }
- }
- }
- /*...*/
- grunt.loadNpmTasks('grunt-contrib-uglify');
- /* 压缩 js*/
- grunt.loadNpmTasks('grunt-contrib-less');
- /* 编译 less*/
建议: 不要在多个时期都绑定同一种任务
推荐: grunt 还有一个插件用于监控文件的修改, 比如兼容 css 文件的修改, 一旦修改了就调用 css 的压缩命令, 详情请参考 grunt-contrib-watch 插件
config.json
config.json 就是以前的 web.config, 但是没有 web.config 拥有各种类型的配置那么强大, 其中各种功能的配置都以代码的形式转移到 Startup.cs 文件中了; 另外一部分信息配置内容, 则放在 config.json 文件中以 json 的格式进行保存
注意, 该文件的信息默认并没有自动加载, 而是需要自己手工加载该配置信息, 代码如下:
- //Startup.cs 类的构造函数中
- Configuration = new Configuration()
- .AddJsonFile("config.json")
- .AddEnvironmentVariables();
通过 Configuration 实例加载该配置文件, 保存在 Configuration 属性中, 以便可以在其它地方进行使用, 而使用时候的 key 值, 则是按照层级来定义的, 以如下默认内容来说:
- {
- "AppSettings": {
- "SiteTitle": "WebDemo01"
- }
- }
要获取链接字符串, 则需要使用如下 key 值:
var connString = Configuration.Get("AppSettings:SiteTitle");
使用起来, 没有 web.config 方便了, 但是为了兼容其它操作系统, 只能这样了
注意: 在 ASP.NET5 中, 配置信息不仅支持 json 格式, 还支持 inixml 等格式, 具体详细请参考后续的配置信息管理章节
Startup.cs
Startup.cs 是整个程序的启动入口, 类似于 Global.asax, 和 Global.asax 文件一样, 起到全局配置信息的作用我们来分析一下该文件的几个重要作用
首先在构造函数中初始化基础配置信息(关于详细的配置信息内容请参考配置信息管理章节), 注意这里的初始化后的配置信息绑定到一个 Configuration 属性上了, 以便另外两个方法在后期进行使用, 如果你要在其它类中进行使用, 则需要将该实例保存到其它地方(如静态变量)
ConfigureServices 方法是依赖注入的核心, 在方法的传入参数 services 中, 首先保存了默认依赖注入里定义的类型定义, 然后我们在这个方法里, 可以继续注册依赖注入的类型定义, 关于依赖注入的详细内容, 可以阅读依赖注入章节
同时一些重要的功能需要开启的话, 也需要在这里开启, 比如添加 Mvc 模块, 就需要使用如下调用语句:
services.AddMvc();
原因是因为, 在新版的 ASP.NET 5 中, 除了最基础的模块以外, 绝大部分模块都是纯组件化的, 这里称为 Middleware, 而组件在使用的时候首先需要先添加该模块才能使用再比如, 添加 EF 模块的话, 需要调用
services.AddEntityFramework()方法
而另外一个 Configure 方法, 顾名思义是对各种 Middleware 组件进行配置的地方, 一般来说, 配置模块的方法都是调用类似 app.UseXXX()这样的方法, 如使用静态文件处理流程, 则调用如下语句即可:
app.UseStaticFiles();
如果要使用 Mvc 的功能, 则需要使用 app.UseMvc 方法, 在调用这些方法时, 可以配置并传入响应的参数
注意, ConfigureServices 里用到的 services.AddXXX()类型的方法和 Configure 方法里用到的 app.UseXXX()类型的方法都是扩展方法, AddXXX()方法是在 IServiceCollection 接口上进行扩展的, 而 UseXXX()方法则是在 IApplicationBuilder 接口上扩展的
关于该文件中提到的依赖注入以及 Configure 方法中的 3 个类型的参数: IApplicationBuilderIHostingEnvironmentILoggerFactory; 我们会在后续的章节中进行详细讲解
其它
通过查看, 可以发现, Views 目录下的 web.config 也被移除了, 在 RC 版中, 要统一引用命名空间, 需要在 _ViewStart.cshtml 或者 _GlobalImport.cshtml 文件中, 后续章节我们会讲到
我们不用做任何修改, F5 运行项目即可运行, 由于默认使用的是 IIS Express, 所以会自动打开新网站首页如果不是 IIS Express, 请阅读后续的编译与部署章节
关于上述 json 配置文件的 scheme 介绍
关于上述配置文件 (含 global.jsonproject.jsonconfig.jsonpackage.jsonbower.json) 的 scheme 定义, 以及 js 参数配置的定义, 请访问
http://schemastore.org/json/
进行查看
来源: http://www.phperz.com/article/18/0321/352691.html