使用 Angular 2 和 Web API 在 ASP.NET Core MVC 中进行 CRUD 操作
一个关于如何使用 ASP.NET CORE、Angular2 和 WebApi 构建 CRUD Web 应用程序的优秀示例
引言
通过本文,您将了解 Web CRUD 应用程序的实现。CRUD 应用程序意味着一个可以对数据源(如数据库、XML 文件等)进行记录的创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete) 的应用程序。
本文的主要目标是教您以下几点:
- 创建 ASP.NET Core MVC 应用程序。
- 使用 npm 安装所需的软件包(Angular2、Typings 等)。
- 对已安装在 SQL Server 上的现有数据库进行反向工程(使用 Entity Framework 模型优先方法)。
- 使用 Web Api 创建一个 RestFul 服务器。
- 在 Angular 2 中创建组件、模板、服务和类。
- 使用 webpack 编译 Angular2 项目。
- 运行 ASP.NETCORE MVC Web 应用程序。
在本文中,我参考了以下链接来构建我的应用程序:
背景
为了更好地理解此演示,最好您对以下内容有扎实的了解:
- C#、JavaScript 和 HTML 编程。
- MVC 架构。
- SQL 语言。
- 数据绑定。
- Entity Framework。
- Visual Studio Code。
必备组件
在开始实现 Web 应用程序之前,您需要安装:
Using the Code
构建 Web 应用程序
在本节中,我将一步一步地解释如何轻松构建一个 CRUD Web 应用程序。
A) 设置 ASP.NETCore MVC 项目
打开您的 CMD 控制台(我建议您以管理员身份运行),然后运行以下命令:
mkdir dotnetcoreapp.
cd dotnetcoreapp.
dotnet new -t web
用于创建 Web 应用程序。dotnet restore
用于加载依赖项。dotnet run
用于启动应用程序。
结果将如下面的图片所示:
使用浏览器导航到给定的 URL (https://:5000),您应该会看到与下图相同的显示:
B) 创建和配置 Angular2 项目
根据官方 Angular2 文档,您需要创建相同的配置文件。
- package.json
- tsconfig.json
- typings.json
接下来,您应该使用 npm
安装 typescript
、typings
和 webpack
。
Npm install –g typescript
Npm install -g typings
Npm install -g webpack
最后,您需要对 Startup.cs 文件进行一些更改,以支持 Angular2 单页应用程序。您需要将默认的 ASP.NETCore MVC 路由指向 Angular2 项目的 index 页(wwwroot/index.html)。为此,您需要在 Configure
方法中进行一些更改,特别是在路由代码中。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
//read error details
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
//point on the index page of the Angular2 project
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404
&& !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/index.html";
await next();
}
});
app.UseStaticFiles();
app.UseMvc();
}
C) 设置数据库
1) 创建数据库
以下步骤将帮助您创建一个非空数据库(在我的例子中,我使用了 SQL Server 2014 来本地托管我的数据库)。
- 在您的 SQL Server 中创建一个名为
DataBaseDotnetCore
的新数据库。 - 执行以下 SQL 脚本来创建一个名为
Product
的表并插入一些数据。CREATE TABLE [dbo].[Product]( [id] [int] IDENTITY(1,1) NOT NULL, [_name] [varchar](250) NULL, [_description] [varchar](250) NULL, Primary key(id), ); insert into Product values ('Juice','Juice description'), _ ('Orange','Orange description')
2) 使用 Entity Framework 模型优先方法
在本节中,您将进行反向工程,以从现有数据库创建 Entity Framework 模型
。为此,您需要遵循以下步骤:
- 导航到应用程序的根文件夹(dotnetcoreapp 文件夹)。
- 导入所需的依赖项和工具:您需要修改 project.json 文件,方法是:
- 添加以下依赖项:
"Microsoft.EntityFrameworkCore":"1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0", "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
- 添加工具:
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
- 保存更改并执行以下命令行:
- 添加以下依赖项:
dotnet -restore
- 编写以下命令行以启动反向工程过程:
dotnet ef dbcontext scaffold "Server=[serverName];Database=[databaseName]; Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models
例如
dotnet ef dbcontext scaffold "Server=LFRUL-013;Database=DataBaseDotnetCore; Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models
- 最后,修改 Startup.cs 文件中的
ConfigureServices
方法以创建数据库连接。public void ConfigureServices(IServiceCollection services) { var connection = @"Server=LFRUL-013;Database=DataBaseDotnetCore;Trusted_Connection=True;"; services.AddDbContext<DataBaseDotnetCoreContext>(options => options.UseSqlServer(connection)); services.AddMvc(); }
D) 设置 RESTful Web API
在此步骤中,您将创建一个名为 ProductsController.cs 的控制器,该控制器实现了几个 HTTP verb:
Get
:从数据库检索product
列表。Post
:创建一个新的Product
。Put
:更新指定的Product
。Delete
:通过传入的 id 删除指定的Product
。
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using angularapp.Models;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace WebApplication
{
[Route("api/[controller]")]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true, Duration = -1)]
public class ProductsController : Controller
{
private DataBaseDotnetCoreContext _context;
public ProductsController(DataBaseDotnetCoreContext context)
{
_context = context;
}
[HttpGet]
public IEnumerable<dynamic> Get()
{
return _context.Product.ToList();
}
[HttpPost]
public string Post([FromBody] Product product)
{
Response.StatusCode = 200;
try{
angularapp.Models.Product newProduct = new Product();
newProduct.Name = product.Name;
newProduct.Description = product.Description;
_context.Product.Add(newProduct);
_context.SaveChanges();
}catch(Exception e){
Response.StatusCode = 400;
return e.ToString();
}
return "OK";
}
[HttpPut]
public string Put([FromBody] Product product)
{
Response.StatusCode = 200;
try{
product.Name = product.Name;
product.Description = product.Description;
_context.Product.Attach(product);
_context.Entry(product).State = EntityState.Modified;
_context.SaveChanges();
}catch(Exception e){
Response.StatusCode = 400;
return e.ToString();
}
return "OK";
}
[HttpDelete]
public String Delete(int id)
{
Response.StatusCode = 200;
try{
angularapp.Models.Product newProduct = new Product();
newProduct.Id = id;
_context.Product.Remove(newProduct);
_context.SaveChanges();
}catch(Exception e){
return e.ToString();
}
return "OK";
}
}
}
当您在此级别运行项目时,实现的 Web API 将在以下 URL 上公开:https://:5000/api/products
E) 设置 Angular2 项目(前端部分)
在 wwwroot/app 文件夹中,创建以下文件:
a) product.service.ts
这是 Angular 服务类,它实现了所需的方法和接口,以确保与已开发的 Web API 的通信。它由以下函数组成:
AnnounceChange
:向观察者发送通知,以在视图列表中刷新数据。LoadData
:从数据库加载数据,通过调用现有的 Web 服务 [/api/products]。Add
:调用外部 Web 服务 [/api/products],该服务将一个新的Product
对象添加到数据库中。Update
:通过调用现有的 Web 服务 [/api/products],更新数据库中现有产品的某些内容。Delete
:通过调用现有的 Web 服务 [/api/products],从数据库中删除指定的 product。
import { Injectable } from '@angular/core';
import { Http, Response, RequestOptions, Headers } from '@angular/http';
import { Observable, Subject } from 'rxjs/Rx';
import 'rxjs/add/operator/toPromise';
@Injectable()
export class ProductService {
constructor(private _http: Http) { }
private RegenerateData = new Subject<number>();
// Observable string streams
RegenerateData$ = this.RegenerateData.asObservable();
AnnounceChange(mission: number) {
this.RegenerateData.next(mission);
}
LoadData(): Promise<iproduct[]> {
return this._http.get('/api/products')
.toPromise()
.then(response => this.extractArray(response))
.catch(this.handleErrorPromise);
}
Add(model) {
let headers = new Headers({ 'Content-Type':
'application/json; charset=utf-8' });
let options = new RequestOptions({ headers: headers });
delete model["id"];
let body = JSON.stringify(model);
return this._http.post('/api/products/', body,
options).toPromise().catch(this.handleErrorPromise);
}
Update(model) {
let headers = new Headers({ 'Content-Type':
'application/json; charset=utf-8' });
let options = new RequestOptions({ headers: headers });
let body = JSON.stringify(model);
return this._http.put('/api/products/', body,
options).toPromise().catch(this.handleErrorPromise);
}
Delete(id: number) {
return this._http.delete('/api/products/?id=' +
id).toPromise().catch(this.handleErrorPromise);
}
protected extractArray(res: Response, showprogress: boolean = true) {
let data = res.json();
return data || [];
}
protected handleErrorPromise(error: any): Promise<void> {
try {
error = JSON.parse(error._body);
} catch (e) {
}
let errMsg = error.errorMessage
? error.errorMessage
: error.message
? error.message
: error._body
? error._body
: error.status
? `${error.status} - ${error.statusText}`
: 'unknown server error';
console.error(errMsg);
return Promise.reject(errMsg);
}
}
b) IProduct.ts
构成 Product
实体的 interface
。
export interface IProduct { id : number , name : string , description : string }
c) app.componentHW.ts
这是主组件。它包含应用程序的模板和实现。
-
refresh
:通过调用_service
变量的 "loadData
method" 从外部服务接收数据,刷新现有的 product 视图列表。 onUpdate
:通过调用 "_service
variable" 的 "Update method" 更新数据库中现有的 product。onDelete
:通过调用 "_service
variable" 的 "Delete
method" 删除由其 "唯一键" 标识的现有 product。
import { Component, OnInit } from '@angular/core';
import { ProductService, IProduct } from './product.service';
import { ProductForm } from './productForm';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'myhw',
template: `
<div class='row'>
<pform></pform>
</div>
<div class='row'>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class='panel-heading'>Products List</div>
<div class='panel-body'>
<table class='table table-condensed'>
<thead>
<th>Id</th>
<th>Name</th>
<th>Description</th>
<th></th>
</thead>
<tbody>
<tr *ngFor="let person of persons" >
<td> {{person.id}} </td>
<td> <input type="text"
[(ngModel)]="person.name"
name="pname"
class="form-control" /> </td>
<td> <input type="text"
[(ngModel)]="person.description"
name="pdescription"
class="form-control" /> </td>
<td> <input type="button"
value="update" class="btn btn-default"
(click)="onUpdate(person)"/>
<input type="button" value="remove"
class="btn btn-danger"
(click)="onDelete(person.id)"/></td>
</tr>
<tbody>
</table>
</div>
</div>
</div>
`
})
export class HwComponent extends OnInit {
subscription: Subscription;
refresh(){
this._service.loadData().then(data => {
this.persons = data;
})
}
constructor(private _service: ProductService) {
super();
this.subscription = _service.RegenerateData$.subscribe(
mission => {
console.log("Good !! ", mission);
this.refresh();
});
}
ngOnInit() {
this.Refresh();
}
onUpdate(elem){
console.log(elem);
this._service.Update(elem).then(data => {
})
}
onDelete(elem : number){
console.log("Delete Form ! ");
console.log(elem);
this._service.Delete(elem).then(data => {
this.Refresh();
})
}
persons: IProduct[] = [];
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
d) Product.ts
此类包含 product
项的详细信息。
export class Product {
constructor(
public id : number,
public name : string,
public description : string
){
}
}
e) productForm.component.html
这是我们 Product
表单使用的 HTML 模板。
<div>
<h3>Product Form</h3>
<form>
<div class="form-group">
<label for="name">Name *</label>
<input type="text" class="form-control"
[(ngModel)]="model.name" name="name" required>
</div>
<div class="form-group">
<label for="description">Description *</label>
<input type="text" class="form-control"
[(ngModel)]="model.description" name="email">
</div>
<button type="button" (click)="onSubmit()"
class="btn btn-primary">Add</button>
</form>
</div>
f) productForm.ts
构成 productForm
模板的后台代码,您将在其中实现:
onSumbit method
:此事件用于调用ProductService
的Add
方法。model
attribute
:用于将产品表单字段与模型绑定。
import { Component, OnInit } from '@angular/core';
import { Product } from './Product';
import { ProductService, IProduct } from './product.service';
@Component({
moduleId: __filename,
selector: 'pform',
templateUrl: './app/productForm.component.html'
})
export class ProductForm {
constructor(private _service: ProductService) {
}
model = new Product(0,'','');
submitted = false;
onSubmit() {
console.log("Sumbitted Form ! ");
this.submitted = true;
this._service.Add(this.model).then(data => {
this._service.AnnounceChange(1212);
})
}
// TODO: Remove this when we're done
get diagnostic() { return JSON.stringify(this.model); }
}
g) app.module.ts
此文件将用于:
- 通过 "
imports
" 关键字导入所需的 Angular2 模块。 - 通过 "
declarations
" 关键字声明创建的组件。 - 通过 "
providers
" 关键字声明所需的全局服务。 - 在 "
bootstrap
" 数组中指定根组件,该组件必须包含在index.html
文件中。
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { HwComponent } from './app.componentHW';
import { ProductService } from './product.service';
import {ProductForm } from './productForm';
@NgModule({
imports: [ BrowserModule,
FormsModule,
HttpModule],
declarations: [ HwComponent, ProductForm],
providers: [
ProductService
],
bootstrap: [ HwComponent]
})
export class AppModule { }
导航到 wwwroot 文件夹,您将在其中开始配置前端项目。
h) systemjs.config.js
此文件用于加载使用 TypeScript
编译器编译的模块。
/**
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': '../node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser':
'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic':
'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
},
meta: {
'./app/main.js': {
format: 'global'
}
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);
i) index.html
此 HTML 文件是应用程序的入口点,我们将在此包含所有必需的 JS、CSS 文件来渲染我们的组件。
<html>
<head>
<title>Angular 2 QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/site.css">
<link rel="stylesheet" href="css/bootstrap.min.css">
<!-- 1. Load libraries -->
<script src="js/core.js"></script>
<script src="js/zone.js"></script>
<script src="js/reflect.js"></script>
<script src="js/system.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<div class="container">
<myhw>Loading ...</myhw>
<div>
</body>
</html>
最后,您应该配置 webpack
来:
- 将所需的
node_modules
导出到 wwwroot/js 文件夹。 - 将
main.ts
转译为名为 main.js 的 JavaScript 文件。
为此,请导航到根文件夹(dotnetcoreapp/),然后创建 webpack.config.js。
module.exports = [
{
entry: {
core: './node_modules/core-js/client/shim.min.js',
zone: './node_modules/zone.js/dist/zone.js',
reflect: './node_modules/reflect-metadata/Reflect.js',
system: './node_modules/systemjs/dist/system.src.js'
},
output: {
filename: './wwwroot/js/[name].js'
},
target: 'web',
node: {
fs: "empty"
}
},
{
entry: {
app: './wwwroot/app/main.ts'
},
output: {
filename: './wwwroot/app/main.js'
},
devtool: 'source-map',
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' }
]
}
}];
F) 运行 Web 应用程序
要运行演示,您应该使用 CMD
编写以下命令行,但首先请确保您位于 .net core Web 应用程序的根目录下:
webpack
:将 TS 文件转译为 JavaScript 文件。dotnet run
:编译项目并运行服务器。
如果编译成功,您可以在浏览器中打开给定的 URL 来查看正在运行的 Web 应用程序。
参考文献
关注点
希望您喜欢这篇帖子。尝试下载源代码,不要犹豫留下您的问题和评论。
历史
- v1 30/10/2016:初始版本