ASP.NET Core RC2 使用 WEB API 和 AngularJS - 第一部分






4.94/5 (23投票s)
在本文中,您将了解 ASP.NET Core RC2 如何使用 WEB API 和 AngularJS。
引言
在本文中,我们将详细介绍如何使用 WEB API 和 AngularJS 创建 ASP.NET Core RC2。
关键点仍然是使用 .NET Framework 4.5.2 而不是 .NET Core。
您也可以查看我的下一篇文章
- 如何替换 ASP.NET Core 的默认 Views 位置?
- 如何将 ASP.NET Core RC2 部署到 IIS?
- 如何将 NLog 集成到 ASP.NET Core 项目?
必备组件
Visual Studio 2015:您可以从 此处 下载。
.NET Core SDK:请从此链接 下载 .NET Core SDK。
.NET Framework 4.5.2:请从此链接 下载 .NET Framework 4.5.2。
.NodeJS:请从此链接 下载 NodeJS。
源代码:请从此链接 下载源代码。
在本文中,我们将详细介绍如何
- 创建我们的 ASP.NET Core RC 2 Web 应用程序。
- 如何添加我们的 WEB API 控制器。
- 如何使用 NPM 添加 Javascript 包。
- 如何配置 Gulp 文件。
- 如何使用 Visual Studio 任务运行器资源管理器运行 Gulp 文件
- 如何创建我们的 AngularJS 模块、控制器和服务文件,并获取 WEB API 数据以绑定到 MVC 页面。
下一部分将包含更多概念:第二部分
- 身份验证
- 使用 json 文件模拟服务 API
使用代码
创建数据库
在此版本中,我不会使用数据库,将其留到下一个版本。只是让它非常非常简单。
创建我们的 ASP.NET Core RC 2 Web 应用程序
安装完 Visual Studio 2015 和 .NET Core SDK 后,请运行您的 Visual Studio 2015。点击新建,然后选择项目,选择 Web,然后选择 ASP.NET Web 应用程序。输入您的项目名称,然后点击确定。
让我们稍微了解一下我的项目/解决方案名称
- 解决方案名称:AspNetCoreSPA
- 项目名称:AspNetCoreSPA.Web
这是我创建新 Web 项目时的规则。解决方案名称代表整个项目,应该简单且单一(不包含任何点)。项目名称代表每个项目的用途(在此,它是 MVC 的 Web)。项目名称以后可以更多,例如:AspNetCoreSPA.Business、AspNetCoreSPA.DataAccess、AspNetCoreSPA.Core。我将写另一篇文章,介绍使用 .NET Core 创建包含多个项目的解决方案。
然后选择“空”模板。我们将从头开始,以便了解我们将安装什么,我们将使用什么。
项目创建完成后,我们将创建如下的文件夹结构
我将详细解释我为什么创建这种结构
- wwwroot 将包含所有客户端代码(js、html、css)。
- 一个重要的提示:我们将不使用“.cshtml”(听起来很疯狂吧?),我们将只使用“.html”,而 AspNet 仅用作数据服务。
- app:包含 html + js 文件(控制器、视图、模板等)。
- styles:包含 css 文件
- images:图片文件
- scripts:包含我们自己的 js 文件:通用 js、...等
- libraries:包含库。这些库是从 node_modules 复制的(详情)。
- Configurations:代表任何配置类。
- Controllers:包含 ASP.NET 控制器。
project.json 文件内容(什么是 project.json 文件?)
{
"userSecretsId": "aspnet-AspNetCoreSPA-c23d27a4-eb88-4b18-9b77-2a93f3b15119",
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Razor.Tools": {
"version": "1.0.0-preview1-final",
"type": "build"
},
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
"Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-final",
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
"version": "1.0.0-preview1-final",
"type": "build"
},
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
"version": "1.0.0-preview1-final",
"type": "build"
}
},
"tools": {
"Microsoft.AspNetCore.Razor.Tools": {
"version": "1.0.0-preview1-final",
"imports": "portable-net45+win8+dnxcore50"
},
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-preview1-final",
"imports": "portable-net45+win8+dnxcore50"
},
"Microsoft.Extensions.SecretManager.Tools": {
"version": "1.0.0-preview1-final",
"imports": "portable-net45+win8+dnxcore50"
},
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
"version": "1.0.0-preview1-final",
"imports": [
"portable-net45+win8+dnxcore50",
"portable-net45+win8"
]
}
},
"frameworks": {
"net452": { }
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"publishOptions": {
"include": [
"wwwroot",
"appsettings.json",
"web.config"
]
},
"scripts": {
"prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ],
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}
创建 package.json(什么是 package.json 文件?):您可以理解 package.json 类似于 Nuget Package Manger。在使用 npm 命令之前,您应该先了解一点 NodeJS。
package.json 的内容代表我们为此项目使用的库。
- angular: 1.5.5
- bootstrap: 3.3.6
- devDependencies 代表我们在开发时使用的库。
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"dependencies": {
},
"devDependencies": {
"gulp": "3.8.11",
"gulp-inject": "4.1.0",
"stream-series": "0.1.1",
"wiredep": "4.0.0"
}
}
也创建 bower.json:添加新项 -> Bower 配置文件
{ "name": "asp.net", "private": true, "dependencies": { "jquery": "2.2.4", "bootstrap": "3.3.6", "angular": "1.5.5", "angular-ui-router": "0.3.0" }, "overrides":{ "bootstrap":{ "main": [ "less/bootstrap.less", "dist/css/bootstrap.css", "dist/js/bootstrap.js" ] } } }
为什么使用 Bower?Bower 是一个带有依赖项的 Javascript 包管理器:例如:Bootstrap 依赖于 jquery,...。
请记住更改 .bowerrc 的内容
.bowerrc 文件内容:这意味着 bower.json 中的所有依赖项都将被复制到“wwwroot/libraries”文件夹
{ "directory": "wwwroot/libraries" }
我们将使用wiredep 自动将 bower 库注入到 index.html 中,我们稍后会看到更多细节。
创建 appsettings.json:应用程序设置类似于 ASP.NET MVC 旧版本的 web.config。我稍后将解释如何使用此文件的内容。首先,您可以看到 DefaultConnection 是空的,因为我们没有使用任何数据库连接。
{
"ConnectionStrings": {
"DefaultConnection": ""
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
我们的 Program.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
namespace AspNetCoreSPA.Web
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
我们的 Startup.cs
using AspNetCoreSPA.Web.Configurations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace AspNetCoreSPA.Web
{
public class Startup
{
public IConfigurationRoot Configuration { get; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.ReplaceDefaultViewEngine();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
创建 gulpfile.js:右键单击我们的项目 -> 添加 -> 新项
gulpfile.js 文件内容
/// <binding BeforeBuild='inject:index' /> "use strict"; var gulp = require("gulp"), series = require('stream-series'), inject = require('gulp-inject'), wiredep = require('wiredep').stream; var webroot = "./wwwroot/"; var paths = { ngModule: webroot + "app/**/*.module.js", ngRoute: webroot + "app/**/*.route.js", ngController: webroot + "app/**/*.controller.js", script: webroot + "scripts/**/*.js", style: webroot + "styles/**/*.css" }; gulp.task('inject:index', function () { var moduleSrc = gulp.src(paths.ngModule, { read: false }); var routeSrc = gulp.src(paths.ngRoute, { read: false }); var controllerSrc = gulp.src(paths.ngController, { read: false }); var scriptSrc = gulp.src(paths.script, { read: false }); var styleSrc = gulp.src(paths.style, { read: false }); gulp.src(webroot + 'app/index.html') .pipe(wiredep({ optional: 'configuration', goes: 'here', ignorePath: '..' })) .pipe(inject(series(scriptSrc, moduleSrc, routeSrc, controllerSrc), { ignorePath: '/wwwroot' })) .pipe(inject(series(styleSrc), { ignorePath: '/wwwroot' })) .pipe(gulp.dest(webroot + 'app')); });
关于 gulpfile.js 的更多详细信息
Series:用于订购 Javascript 文件:因为我们在 html 页面中包含 js 文件时,它必须是有序的。
Inject:用于将必要的库包含到我们的 html 页面中。
Wiredep:用于将 bower 库包含到我们的 html 页面中。
var gulp = require("gulp"), series = require('stream-series'), inject = require('gulp-inject'), wiredep = require('wiredep').stream;
现在来看主脚本
gulp.src(webroot + 'app/index.html') .pipe(wiredep({ optional: 'configuration', goes: 'here', ignorePath: '..' })) .pipe(inject(series(scriptSrc, moduleSrc, routeSrc, controllerSrc), { ignorePath: '/wwwroot' })) .pipe(inject(series(styleSrc), { ignorePath: '/wwwroot' })) .pipe(gulp.dest(webroot + 'app'));
第 1 行:嘿 gulp,我们将使用 index.html 页面来注入 js 和 css,看看 index.html 的内容,您就会明白。
第 2 行:按依赖项排序注入 bower 库
<!-- bower:css -->
<!-- endbower -->
<!-- bower:js -->
<!-- endbower -->
第 3 行:注入
<!-- inject:css -->
<!-- endinject -->
<!-- inject:js -->
<!-- endinject -->
创建 index.html:右键单击 app -> 添加 -> 新项 -> HTML 页面 -> index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AspNetCoreSPA</title>
<!-- bower:css -->
<!-- endbower -->
<!-- inject:css -->
<!-- endinject -->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a href="#">AspNetCoreSPA</a>
</div>
</div>
</div>
<div class="container body-content">
<div class="jumbotron">
<h1>AspNetCoreSPA</h1>
<p class="lead">Welcome to AspNetCoreSPA</p>
</div>
<div class="row">
<h2>Student List</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>Doe</td>
<td>john@example.com</td>
</tr>
<tr>
<td>Mary</td>
<td>Moe</td>
<td>mary@example.com</td>
</tr>
<tr>
<td>July</td>
<td>Dooley</td>
<td>july@example.com</td>
</tr>
</tbody>
</table>
</div>
<hr />
<footer>
<p>© 2016 - Toan Manh Nguyen</p>
</footer>
</div>
<!-- bower:js -->
<!-- endbower -->
<!-- inject:js -->
<!-- endinject -->
</body>
</html>
在 wwwroot/styles/site.css 中添加 site.css
body {
padding-top: 50px;
padding-bottom: 20px;
}
/* Wrapping element */
/* Set some basic padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}
/* Set widths on the form inputs since otherwise they're 100% wide */
input,
select,
textarea {
max-width: 280px;
}
/* Styles for angular ui transition animations */
.angular-animation-container {
position: relative;
}
.shuffle-animation.ng-enter,
.shuffle-animation.ng-leave {
position: absolute;
}
.shuffle-animation.ng-enter {
-moz-transition: ease-out all 0.3s 0.4s;
-o-transition: ease-out all 0.3s 0.4s;
-webkit-transition: ease-out all 0.3s 0.4s;
-ms-transition: ease-out all 0.3s 0.4s;
transition: ease-out all 0.3s 0.4s;
left: 2em;
opacity: 0;
}
.shuffle-animation.ng-enter.ng-enter-active {
left: 0;
opacity: 1;
}
.shuffle-animation.ng-leave {
-moz-transition: 0.3s ease-out all;
-o-transition: 0.3s ease-out all;
-webkit-transition: 0.3s ease-out all;
-ms-transition: 0.3s ease-out all;
transition: 0.3s ease-out all;
left: 0;
opacity: 1;
}
.shuffle-animation.ng-leave.ng-leave-active {
left: 2em;
-ms-opacity: 0;
opacity: 0;
}
创建 HomeController 以返回 index.html:右键单击 Controllers 文件夹 -> 添加 -> 新项 -> Class -> HomeController.cs
using Microsoft.AspNetCore.Mvc;
namespace AspNetCoreSPA.Web.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View("index");
}
}
}
然后转到 View -> Other Windows -> Task Runner Explorer 查看 Gulpfile.js 的任务
然后右键单击 lib 并运行
运行 gulp 任务后,index.html 会发生一点变化
在 <head> 中
<!-- bower:css -->
<link rel="stylesheet" href="/libraries/bootstrap/dist/css/bootstrap.css" />
<!-- endbower -->
<!-- inject:css -->
<link rel="stylesheet" href="/styles/site.css">
<!-- endinject -->
在 <body> 中
<!-- bower:js -->
<script src="/libraries/jquery/dist/jquery.js"></script>
<script src="/libraries/bootstrap/dist/js/bootstrap.js"></script>
<script src="/libraries/angular/angular.js"></script>
<script src="/libraries/angular-ui-router/release/angular-ui-router.js"></script>
<!-- endbower -->
<!-- inject:js -->
<script src="/scripts/site.common.js"></script>
<script src="/scripts/site.ng.js"></script>
<script src="/app/index.module.js"></script>
<script src="/app/index.route.js"></script>
<script src="/app/main/main.controller.js"></script>
<!-- endinject -->
酷!Gulp 自动将必要的代码按顺序注入到 index.html 页面中。
现在,我们来谈谈 AspNetCore 的默认 Views 文件夹
- AspNetCore 的默认 Views 文件夹是:
-
"/Views/{1}/{0}" + ViewExtension" "/Views/Shared/{0}" + ViewExtension"
- 但我们的视图位于 wwwroot/app,那么 AspNetCore 如何检测我们的视图呢?我们有一个 Bug在这里。解决方案如下
- 扩展 RazorViewEngine
- 将 IRazorViewEngine 的实例替换为 MyRazorViewEngine,我们的新位置是
-
"~/wwwroot/app/views/{1}/{0}.html" "~/wwwroot/app/{0}.html"
- 您可以在上面链接的 github 中的我的代码中看到详细信息
现在,我们将 html 分割为主,index.html 只包含 css、js 和 body 标签。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AspNetCoreSPA</title>
<script type="text/javascript">
var site = site || {};
</script>
<!-- bower:css -->
<!-- endbower -->
<!-- inject:css -->
<!-- endinject -->
</head>
<body ng-app="app">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a href="#">AspNetCoreSPA</a>
</div>
</div>
</div>
<div class="container body-content">
<div class="jumbotron">
<h1>AspNetCoreSPA</h1>
<p class="lead">Welcome to AspNetCoreSPA</p>
</div>
<div class="row">
<div ui-view></div>
</div>
<hr />
<footer>
<p>© 2016 - Toan Manh Nguyen</p>
</footer>
</div>
<!-- bower:js -->
<!-- endbower -->
<!-- inject:js -->
<!-- endinject -->
</body>
</html>
我们在 main 文件夹中创建一个 main.html 并将代码放入其中
main.html
<h2>Student List</h2> <table class="table table-striped"> <thead> <tr> <th>Firstname</th> <th>Lastname</th> <th>Email</th> </tr> </thead> <tbody> <tr> <td>John</td> <td>Doe</td> <td>john@example.com</td> </tr> <tr> <td>Mary</td> <td>Moe</td> <td>mary@example.com</td> </tr> <tr> <td>July</td> <td>Dooley</td> <td>july@example.com</td> </tr> </tbody> </table>
我们创建 index.module.js 来配置 ui.router
(function () { 'use strict'; angular .module('app', [ 'ui.router' ]); })();
我们创建 index.route.js
(function () { 'use strict'; angular .module('app') .config(routerConfig); routerConfig.$inject = ['$stateProvider', '$urlRouterProvider']; function routerConfig($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/'); $stateProvider .state('home', { url: '/', templateUrl: 'app/main/main.html', controller: 'MainController', controllerAs: 'mainCtrl' }); } })();
现在让我们再次看看整个项目结构
再次,您可以在此处下载源代码。
让我们按 F5 键查看结果。
历史
2016/05/26 - 创建文章。
2016/05/28 - 重构文章并添加有关 Gulpfile.js 的更多详细信息
- 我更改了 wwwroot 中的一些文件夹名称,例如 YeoMan 文件夹结构。请参考我的GitHub