使用ASP.NET Web API和Web API Client Gen使Angular 2应用程序的开发更加高效
本文介绍“ 为ASP.NET Web API生成TypeScript客户端API ”,重点介绍Angular 2+代码示例和各自的SDLC。如果您正在开发.NET Core Web API后端,则可能需要阅读为ASP.NET Core Web API生成C#Client API。
背景
自WebApiClientGen
Angular 2仍然在RC2时,自2016年6月v1.9.0-beta 以来,对Angular2的支持已经可用。并且在WebApiClientGen
v2.0中提供了对Angular 2产品发布的支持。希望NG2的发展不会如此频繁地破坏我的CodeGen和我的Web前端应用程序。:)
在2016年9月底发布Angular 2的第一个产品发布几周后,我碰巧启动了一个使用Angular2的主要Web应用程序项目,因此我WebApiClientGen
对NG2应用程序开发的使用方法几乎相同。
推定
- 您正在开发ASP.NET Web API 2.x应用程序,并将基于Angular 2+为SPA开发TypeScript库。
- 您和其他开发人员喜欢在服务器端和客户端都通过强类型数据和函数进行高度抽象。
- Web API和实体框架代码优先使用POCO类,您可能不希望将所有数据类和成员发布到客户端程序源码。
并且可选地,如果您或您的团队支持基于Trunk的开发,那么更好,因为使用的设计WebApiClientGen
和工作流程WebApiClientGen
假设基于Trunk的开发,这比其他分支策略(如Feature Branching和GitFlow等)更有效。对于熟练掌握TDD的团队。
为了跟进这种开发客户端程序的新方法,最好有一个ASP.NET Web API项目。您可以使用现有项目,也可以创建演示项目。
使用代码
本文重点介绍Angular 2+的代码示例。假设您有一个ASP.NET Web API项目和一个Angular2项目作为VS解决方案中的兄弟项目。如果你将它们分开,那么为了使开发步骤无缝地编写脚本应该不难。
我认为您已阅读“ 为ASP.NET Web API生成TypeScript客户端API ”。为jQuery生成客户端API的步骤几乎与为Angular 2生成客户端API的步骤相同。演示TypeScript代码基于TUTORIAL:TOUR OF HEROES,许多人从中学习了Angular2。因此,您将能够看到如何WebApiClientGen
适应并改进Angular2应用程序的典型开发周期。
这是Web API代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Runtime.Serialization;
using System.Collections.Concurrent; namespace DemoWebApi.Controllers
{
[RoutePrefix("api/Heroes")]
public class HeroesController : ApiController
{
public Hero[] Get()
{
return HeroesData.Instance.Dic.Values.ToArray();
} public Hero Get(long id)
{
Hero r;
HeroesData.Instance.Dic.TryGetValue(id, out r);
return r;
} public void Delete(long id)
{
Hero r;
HeroesData.Instance.Dic.TryRemove(id, out r);
} public Hero Post(string name)
{
var max = HeroesData.Instance.Dic.Keys.Max();
var hero = new Hero { Id = max + , Name = name };
HeroesData.Instance.Dic.TryAdd(max + , hero);
return hero;
} public Hero Put(Hero hero)
{
HeroesData.Instance.Dic[hero.Id] = hero;
return hero;
} [HttpGet]
public Hero[] Search(string name)
{
return HeroesData.Instance.Dic.Values.Where(d => d.Name.Contains(name)).ToArray();
}
} [DataContract(Namespace = DemoWebApi.DemoData.Constants.DataNamespace)]
public class Hero
{
[DataMember]
public long Id { get; set; } [DataMember]
public string Name { get; set; }
} public sealed class HeroesData
{
private static readonly Lazy<HeroesData> lazy =
new Lazy<HeroesData>(() => new HeroesData()); public static HeroesData Instance { get { return lazy.Value; } } private HeroesData()
{
Dic = new ConcurrentDictionary<long, Hero>(new KeyValuePair<long, Hero>[] {
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Mr. Nice" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Narco" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Bombasto" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Celeritas" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Magneta" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="RubberMan" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Dynama" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Dr IQ" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Magma" }),
new KeyValuePair<long, Hero>(, new Hero {Id=, Name="Tornado" }), });
} public ConcurrentDictionary<long, Hero> Dic { get; private set; }
}
}
步骤0:将NuGet包WebApiClientGen安装到Web API项目
安装还将安装依赖的NuGet包Fonlow.TypeScriptCodeDOM
和Fonlow.Poco2Ts
项目引用。
此外,用于触发CodeGen的CodeGenController.cs被添加到Web API项目的Controllers文件夹中。
将CodeGenController
只在调试版本开发过程中应该是可用的,因为客户端API应该用于Web API的每个版本生成一次。
提示
如果您正在使用@ angular / http中定义的Angular2的Http服务,那么您应该使用WebApiClientGen
v2.2.5。如果您使用的HttpClient
是@ angular / common / http中定义的Angular 4.3中可用的服务,并且在Angular 5中已弃用,那么您应该使用WebApiClientGen
v2.3.0。
第1步:准备JSON配置数据
下面的JSON配置数据是POST
CodeGen Web API:
{
"ApiSelections": {
"ExcludedControllerNames": [
"DemoWebApi.Controllers.Account"
], "DataModelAssemblyNames": [
"DemoWebApi.DemoData",
"DemoWebApi"
],
"CherryPickingMethods":
}, "ClientApiOutputs": {
"ClientLibraryProjectFolderName": "DemoWebApi.ClientApi",
"GenerateBothAsyncAndSync": true, "CamelCase": true,
"TypeScriptNG2Folder": "..\\DemoAngular2\\clientapi",
"NGVersion" : }
}
提示
Angular 6正在使用RxJS v6,它引入了一些重大变化,特别是对于导入Observable
。默认情况下,WebApiClientGen
2.4和更高版本默认将导入声明为import { Observable } from 'rxjs';
。如果您仍在使用Angular 5.x,则需要"NGVersion" : 5
在JSON配置中声明,因此生成的代码中的导入将是更多详细信息,import { Observable } from 'rxjs/Observable'; .
请参阅RxJS v5.x至v6更新指南和RxJS:版本6的TSLint规则。
备注
您应确保“ TypeScriptNG2Folder
”存在的文件夹存在,因为WebApiClientGen
不会为您创建此文件夹,这是设计使然。
建议到JSON配置数据保存到与文件类似的这一个位于Web API项目文件夹。
如果您在Web API项目中定义了所有POCO类,则应将Web API项目的程序集名称放在“ DataModelAssemblyNames
” 数组中。如果您有一些专用的数据模型程序集可以很好地分离关注点,那么您应该将相应的程序集名称放入数组中。您可以选择为jQuery或NG2或C#客户端API代码生成TypeScript客户端API代码,或者全部三种。
“ TypeScriptNG2Folder
”是Angular2项目的绝对路径或相对路径。例如,“ .. \\ DemoAngular2 \\ ClientApi ”表示DemoAngular2
作为Web API项目的兄弟项目创建的Angular 2项目“ ”。
在CodeGen
根据“从POCO类生成强类型打字稿接口CherryPickingMethods
,其在下面的文档注释描述”:
/// <summary>
/// Flagged options for cherry picking in various development processes.
/// </summary>
[Flags]
public enum CherryPickingMethods
{
/// <summary>
/// Include all public classes, properties and properties.
/// </summary>
All = , /// <summary>
/// Include all public classes decorated by DataContractAttribute,
/// and public properties or fields decorated by DataMemberAttribute.
/// And use DataMemberAttribute.IsRequired
/// </summary>
DataContract =, /// <summary>
/// Include all public classes decorated by JsonObjectAttribute,
/// and public properties or fields decorated by JsonPropertyAttribute.
/// And use JsonPropertyAttribute.Required
/// </summary>
NewtonsoftJson = , /// <summary>
/// Include all public classes decorated by SerializableAttribute,
/// and all public properties or fields
/// but excluding those decorated by NonSerializedAttribute.
/// And use System.ComponentModel.DataAnnotations.RequiredAttribute.
/// </summary>
Serializable = , /// <summary>
/// Include all public classes, properties and properties.
/// And use System.ComponentModel.DataAnnotations.RequiredAttribute.
/// </summary>
AspNet = ,
}
默认选项是DataContract
选择加入。您可以使用任何方法或组合方法。
第2步:运行Web API项目的DEBUG构建
步骤3:POST JSON配置数据以触发客户端API代码的生成
在IIS Express上的IDE中运行Web项目。
然后使用Curl或Poster或任何您喜欢的客户端工具POST到http:// localhost:10965 / api / CodeGen,with content-type=application/json
。
提示
基本上,每当Web API更新时,您只需要步骤2来生成客户端API,因为您不需要每次都安装NuGet包或创建新的JSON配置数据。
编写一些批处理脚本来启动Web API和POST JSON配置数据应该不难。为了您的方便,我实际起草了一个:Powershell脚本文件CreateClientApi.ps1,它在IIS Express上启动Web(API)项目,然后发布JSON配置文件以触发代码生成。
基本上,您可以制作Web API代码,包括API控制器和数据模型,然后执行CreateClientApi.ps1。而已!WebApiClientGen
和CreateClientApi.ps1将为您完成剩下的工作。
发布客户端API库
现在您在TypeScript中生成了客户端API,类似于以下示例:
import { Injectable, Inject } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
export namespace DemoWebApi_DemoData_Client {
export enum AddressType {Postal, Residential} export enum Days {Sat=, Sun=, Mon=, Tue=, Wed=, Thu=, Fri=} export interface PhoneNumber {
fullNumber?: string;
phoneType?: DemoWebApi_DemoData_Client.PhoneType;
} export enum PhoneType {Tel, Mobile, Skype, Fax} export interface Address {
id?: string;
street1?: string;
street2?: string;
city?: string;
state?: string;
postalCode?: string;
country?: string;
type?: DemoWebApi_DemoData_Client.AddressType;
location?: DemoWebApi_DemoData_Another_Client.MyPoint;
} export interface Entity {
id?: string;
name: string;
addresses?: Array<DemoWebApi_DemoData_Client.Address>;
phoneNumbers?: Array<DemoWebApi_DemoData_Client.PhoneNumber>;
} export interface Person extends DemoWebApi_DemoData_Client.Entity {
surname?: string;
givenName?: string;
dob?: Date;
} export interface Company extends DemoWebApi_DemoData_Client.Entity {
businessNumber?: string;
businessNumberType?: string;
textMatrix?: Array<Array<string>>;
int2DJagged?: Array<Array<number>>;
int2D?: number[][];
lines?: Array<string>;
} export interface MyPeopleDic {
dic?: {[id: string]: DemoWebApi_DemoData_Client.Person };
anotherDic?: {[id: string]: string };
intDic?: {[id: number]: string };
}
} export namespace DemoWebApi_DemoData_Another_Client {
export interface MyPoint {
x: number;
y: number;
} } export namespace DemoWebApi_Controllers_Client {
export interface FileResult {
fileNames?: Array<string>;
submitter?: string;
} export interface Hero {
id?: number;
name?: string;
}
} @Injectable()
export class Heroes {
constructor(@Inject('baseUri') private baseUri: string = location.protocol + '//' +
location.hostname + (location.port ? ':' + location.port : '') + '/', private http: Http){
} /**
* Get all heroes.
* GET api/Heroes
* @return {Array<DemoWebApi_Controllers_Client.Hero>}
*/
get(): Observable<Array<DemoWebApi_Controllers_Client.Hero>>{
return this.http.get(this.baseUri + 'api/Heroes').map(response=> response.json());
} /**
* Get a hero.
* GET api/Heroes/{id}
* @param {number} id
* @return {DemoWebApi_Controllers_Client.Hero}
*/
getById(id: number): Observable<DemoWebApi_Controllers_Client.Hero>{
return this.http.get(this.baseUri + 'api/Heroes/'+id).map(response=> response.json());
} /**
* DELETE api/Heroes/{id}
* @param {number} id
* @return {void}
*/
delete(id: number): Observable<Response>{
return this.http.delete(this.baseUri + 'api/Heroes/'+id);
} /**
* Add a hero
* POST api/Heroes?name={name}
* @param {string} name
* @return {DemoWebApi_Controllers_Client.Hero}
*/
post(name: string): Observable<DemoWebApi_Controllers_Client.Hero>{
return this.http.post(this.baseUri + 'api/Heroes?name='+encodeURIComponent(name),
JSON.stringify(null), { headers: new Headers({ 'Content-Type':
'text/plain;charset=UTF-8' }) }).map(response=> response.json());
} /**
* Update hero.
* PUT api/Heroes
* @param {DemoWebApi_Controllers_Client.Hero} hero
* @return {DemoWebApi_Controllers_Client.Hero}
*/
put(hero: DemoWebApi_Controllers_Client.Hero): Observable<DemoWebApi_Controllers_Client.Hero>{
return this.http.put(this.baseUri + 'api/Heroes', JSON.stringify(hero),
{ headers: new Headers({ 'Content-Type': 'text/plain;charset=UTF-8'
}) }).map(response=> response.json());
} /**
* Search heroes
* GET api/Heroes?name={name}
* @param {string} name keyword contained in hero name.
* @return {Array<DemoWebApi_Controllers_Client.Hero>} Hero array matching the keyword.
*/
search(name: string): Observable<Array<DemoWebApi_Controllers_Client.Hero>>{
return this.http.get(this.baseUri + 'api/Heroes?name='+
encodeURIComponent(name)).map(response=> response.json());
}
}
提示
如果您希望生成的TypeScript代码符合JavaScript和JSON的camel大小写,则可以在WebApiConfig
Web API的脚手架代码类中添加以下行:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
然后属性名称和函数名称将在camel大小写中,前提是C#中的相应名称都在Pascal大小写中。有关详细信息,请查看camelCasing或PascalCasing。
客户端应用编程
在像Visual Studio这样的正常文本编辑器中编写客户端代码时,您可能会获得很好的智能感知。
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import * as namespaces from '../clientapi/WebApiNG2ClientAuto';
import DemoWebApi_Controllers_Client = namespaces.DemoWebApi_Controllers_Client; @Component({
moduleId: module.id,
selector: 'my-heroes',
templateUrl: 'heroes.component.html',
styleUrls: ['heroes.component.css']
})
通过IDE进行设计时类型检查,并在生成的代码之上进行编译时类型检查,可以更轻松地提高客户端编程的效率和产品质量。
不要做计算机可以做的事情,让计算机为我们努力工作。我们的工作是为客户提供自动化解决方案,因此最好先自行完成自己的工作。
兴趣点
在典型的角2个教程,包括官方的一个 这已经存档,作者经常督促应用程序开发者制作一个服务类,如“ HeroService
”,而黄金法则是:永远委托给配套服务类的数据访问。
WebApiClientGen
为您生成此服务类DemoWebApi_Controllers_Client.Heroes
,它将使用真正的Web API而不是内存中的Web API。在开发过程中WebApiClientGen
,我创建了一个演示项目DemoAngular2和各自用于测试的Web API控制器。
典型的教程还建议使用模拟服务进行单元测试。WebApiClientGen
使用真正的Web API服务要便宜得多,因此您可能不需要创建模拟服务。您应该在开发期间平衡使用模拟或实际服务的成本/收益,具体取决于您的上下文。通常,如果您的团队已经能够在每台开发机器中使用持续集成环境,那么使用真实服务运行测试可能非常无缝且快速。
在典型的SDLC中,在初始设置之后,以下是开发Web API和NG2应用程序的典型步骤:
- 升级Web API
- 运行CreateClientApi.ps1以更新TypeScript for NG2中的客户端API。
- 使用生成的TypeScript客户端API代码或C#客户端API代码,在Web API更新时创建新的集成测试用例。
- 相应地修改NG2应用程序。
- 要进行测试,请运行StartWebApi.ps1以启动Web API,并在VS IDE中运行NG2应用程序。
提示
对于第5步,有其他选择。例如,您可以使用VS IDE同时以调试模式启动Web API和NG2应用程序。一些开发人员可能更喜欢使用“ npm start
”。
本文最初是为Angular 2编写的,具有Http服务。Angular 4.3中引入了WebApiClientGen
2.3.0支持HttpClient
。并且生成的API在接口级别保持不变。这使得从过时的Http服务迁移到HttpClient服务相当容易或无缝,与Angular应用程序编程相比,不使用生成的API而是直接使用Http服务。
顺便说一句,如果你没有完成向Angular 5的迁移,那么这篇文章可能有所帮助: 升级到Angular 5和HttpClient。如果您使用的是Angular 6,则应使用WebApiClientGen
2.4.0+。
使用ASP.NET Web API和Web API Client Gen使Angular 2应用程序的开发更加高效的更多相关文章
- NET Web API和Web API Client Gen使Angular 2应用程序
使用ASP.NET Web API和Web API Client Gen使Angular 2应用程序的开发更加高效 本文介绍“ 为ASP.NET Web API生成TypeScript客户端API ” ...
- ASP.NET MVC View 和 Web API 的基本权限验证
ASP.NET MVC 5.0已经发布一段时间了,适应了一段时间,准备把原来的MVC项目重构了一遍,先把基本权限验证这块记录一下. 环境:Windows 7 Professional SP1 + Mi ...
- [译]ABP框架使用AngularJs,ASP.NET MVC,Web API和EntityFramework构建N层架构的SPA应用程序
本文转自:http://www.skcode.cn/archives/281 本文演示ABP框架如何使用AngularJs,ASP.NET MVC,Web API 和EntityFramework构建 ...
- ASP.NET MVC4中调用WEB API的四个方法
http://tech.it168.com/a2012/0606/1357/000001357231_all.shtml [IT168技术]当今的软件开发中,设计软件的服务并将其通过网络对外发布,让各 ...
- 如何将一个 ASP.NET MVC 4 和 Web API 项目升级到 ASP.NET MVC 5 和 Web API 2
----转自微软官网www.asp.net/mvc/ ASP.NET MVC 5 和 Web API 2 带来的新功能,包括属性路由. 身份验证筛选器,以及更多的主机.请参阅http://www.as ...
- ASP.NET Web API——选择Web API还是WCF
WCF是.NET平台服务开发的一站式框架,那么为什么还要有ASP.NET Web API呢?简单来说,ASP.NET Web API的设计和构建只考虑了一件事情,那就是HTTP,而WCF的设计主要是考 ...
- 如何使用ASP.NET Web API OData在Oracle中使用Entity Framework 6.x Code-First方式开发 OData V4 Service
环境: Visual Studio 2013 + .Net Framework 4.5.2 1.新建项目 2.安装OData,ODP.NET 安装的包: 下面是部分代码: using System; ...
- 返璞归真 asp.net mvc (11) - asp.net mvc 4.0 新特性之自宿主 Web API, 在 WebForm 中提供 Web API, 通过 Web API 上传文件, .net 4.5 带来的更方便的异步操作
原文:返璞归真 asp.net mvc (11) - asp.net mvc 4.0 新特性之自宿主 Web API, 在 WebForm 中提供 Web API, 通过 Web API 上传文件, ...
- 从头编写 asp.net core 2.0 web api 基础框架 (1)
工具: 1.Visual Studio 2017 V15.3.5+ 2.Postman (Chrome的App) 3.Chrome (最好是) 关于.net core或者.net core 2.0的相 ...
随机推荐
- js默认事件汇总
默认事件 就是浏览器通过HTML标签或DOM元素提供的一些功能性的默认行为.比如在a标签href属性上的跳转,右键呼出的菜单等等.我们可以通过js取消或更改这些默认事件. 取消默认事件 默认事件都是定 ...
- PAT天梯赛L2-008 最长对称字符串
题目链接:点击打开链接 对给定的字符串,本题要求你输出最长对称子串的长度.例如,给定"Is PAT&TAP symmetric?",最长对称子串为"s PAT&a ...
- State模式(状态设计模式)
State??? State模式中,我们用类来表示状态.以类来表示状态后,我们就能通过切换类来方便地改变对象的状态.当需要增加新的状态时,如何修改代码这个问题也会很明确. 直接用状态代替硬编码 依赖于 ...
- chapter05_20180330
// 5.7 主构造器// 主构造器的参数直接旋转在类名之后class Person57(val name: String, val age: Int) { // 上边()中的内容就是主构造器的参数} ...
- java——helloword
第一次接触Java,感觉乱乱的,需要捋清楚一些概念再安装java. 首先,什么是JDK,什么是JRE呢? JRE:JAVA运行环境(Java Runtime Envirnment) JDK:Java开 ...
- Win10创建还原点
Win + E打开任务管理器 右击此电脑,选择属性 单击高级系统设置 选择系统保护 单击系统还原 按照提示操作,OK.
- 剑指Offer——数组中只出现一次的数字(一个很帅的异或解法)
题目: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 看题目脑子里就出现做法了: 遍历,用个HashMap来记录出现的次数,然后再遍历HashMap返回 ...
- 034 Search for a Range 搜索范围
给定一个已经升序排序的整形数组,找出给定目标值的开始位置和结束位置.你的算法时间复杂度必须是 O(log n) 级别.如果在数组中找不到目标,返回 [-1, -1].例如:给出 [5, 7, 7, 8 ...
- 【踩坑】mysql LIMIT 与 ORDER BY位置问题
今天在重做iblog项目后台时,当测试功能,发现后台报错,位置在 MyBatis 向 MySQL 取数据时的语法问题,如下: SELECT * FROM article WHERE authorID ...
- linux解压与参数介绍
linux下 各种解压文件使用方法:https://www.jianshu.com/p/ca41f32420d6 解压参数详解:http://www.cnblogs.com/jyaray/archiv ...