1. 程式人生 > >解讀ASP.NET 5 & MVC6系列(2):初識專案

解讀ASP.NET 5 & MVC6系列(2):初識專案

初識專案

開啟VS2015,建立Web專案,選擇ASP.NET Web Application,在彈出的窗口裡選擇ASP.NET 5 Website模板建立專案,圖示如下:

我們可以看到,此時Web Forms\MVC\Web API複選框都選擇不了,原有是因為在ASP.NET 5中做了大量更改,移除了Web Forms功能,將MVC、Web API、Web 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.jsonNPM管理器的配置檔案,由於在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檔案中,比如你需要的jquery、bootstrap、angular等等,其管理方式與project.json裡的程式集和package.json裡的npm包一樣,都是通過在dependencies節點下宣告包的名稱和版本來實現的。

我們可以在此宣告一個angular包,儲存以後就可以看到在解決方案Dependencie->Bower節點下該angular已經自動下載好了,編譯專案,就可以看到在wwroot/lib也可以看到angular資料夾以及相應的檔案了。

在bower.json還有一個exportsOverride節點非常重要,他擴充套件了原來bower的前端檔案copy機制,預設情況下bower只會複製main節點定義的檔案。但有時候我們要複製的檔案可能不止這些,所以grunt-bower-task外掛就擴充套件了該功能,定義了這個exportsOverride節點,其使用規則如下:

  1. 如果Bower包定義了main檔案節點,就將其複製到wwwroot/lib下。
  2. 如果Bower包定義了的main節點為空,則將整個包的目錄都複製到wwwroot/lib下。
  3. 如果定義了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是一個基於gruntbower管理外掛,用於自動執行bowerinstall命令來安裝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格式,還支援ini、xml等格式,具體詳細請參考後續的配置資訊管理章節。

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個型別的引數:IApplicationBuilder、IHostingEnvironment、ILoggerFactory;我們會在後續的章節中進行詳細講解。

其它

通過檢視,可以發現,Views目錄下的web.config也被移除了,在RC版中,要統一引用名稱空間,需要在 _ViewStart.cshtml 或者 _GlobalImport.cshtml 檔案中,後續章節我們會講到。

我們不用做任何修改,F5執行專案即可執行,由於預設使用的是IIS Express,所以會自動開啟新網站首頁。如果不是IIS Express,請閱讀後續的編譯與部署章節。

關於上述json配置檔案的scheme介紹

關於上述配置檔案(含global.json、project.json、config.json、package.json、bower.json)的scheme定義,以及js引數配置的定義,請訪問http://schemastore.org/json/進行檢視。

推薦資料:

同步與推薦