前几天,按照AngularJS2的英雄指南教程走了一遍,教程网址是http://origin.angular.live/docs/ts/latest/tutorial/

在步骤完成后,又更进一步,在英雄增删改的时候,直接调用.net core的WebApi来实现后台数据的操作,替换教程中的模拟WebApi方式。在替换.net core WebApi时,还是遇到了一些坑的,这里记录一下。

先来看一下WebApi和AngularJS的源代码:

WebApi

     [Route("api/[controller]")]
public class ValuesController : Controller
{
private List<Hero> heroes; public ValuesController()
{
heroes = GetHeroes() } [HttpGet]
public IEnumerable<Hero> Get()
{
return heroes;
} [HttpGet("{id}")]
public Hero Get(int id)
{
return heroes.Single(h => h.Id == id);
} [HttpGet]
[Route("GetList")]
public IEnumerable<Hero> GetList(string key)
{
return heroes.Where(h => h.Name.Contains(key));
} [HttpPost]
public void Post([FromBody]Hero info)
{
Hero hero = new Hero(); hero.Id = heroes.Max(h => h.Id) + ;
hero.Name = info.Name; AddHeroes(hero);
} [HttpPut("{id}")]
public void Put(int id, [FromBody]Hero hero)
{
Hero x = heroes.Single(h => h.Id == id); x.Name = hero.Name; UpdateHeroes(x);
} [HttpDelete("{id}")]
public void Delete(int id)
{
Hero hero = heroes.Single(h => h.Id == id);
RemoveHeroes (hero);
}
}

AngularJS

     getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroUrl).toPromise().then(response => response.json() as Hero[]).catch(this.handleError);
} getHero(id: number): Promise<Hero> {
return this.getHeroes().then(heroes => heroes.find(hero => hero.id === id));
} getHeroesSlowly(): Promise<Hero[]> {
return new Promise<Hero[]>(resolve =>
setTimeout(resolve, 2000)) // delay 2 seconds
.then(() => this.getHeroes());
} updateHero(hero: Hero): Promise<Hero> {
const url = `${this.heroUrl}/${hero.id}`;
return this.http
.put(url, JSON.stringify(hero), { headers: this.headers })
.toPromise()
.then(() => hero)
.catch(this.handleError);
} addHero(heroName: string): Promise<Hero> {
return this.http
.post(this.heroUrl, JSON.stringify({ name: name }), { headers: this.headers })
.toPromise()
.then(response => response.json())
.catch(this.handleError);
} deleteHero(heroId: number): Promise<void> {
const url = `${this.heroUrl}/${heroId}`;
return this.http
.delete(url, { headers: this.headers })
.toPromise()
.then(() => null)
.catch(this.handleError);
} search(term: string): Observable<Hero[]> {
return this.http.get(`${this.heroUrl}/GetList?key=${term}`).map((r: Response) => r.json() as Hero[]);
} private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message | error);
}

一、跨域访问(Cors)问题

在建好WebApi站点,启动运行,一切正常。我们仅显示前两个Action,结果如下:

然而,在AngularJS客户端,出现了问题,界面没有任何英雄出现。

再查Chrome的控制台,发现了问题所在,WebApi的Access-Control-Allow-Origin没有开,也就是跨域访问不通过。

跨域访问,就是JS访问其他网址的信息。例如这里的AngularJS的

     getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroUrl).toPromise().then(response => response.json() as Hero[]).catch(this.handleError);
}

方法中,调用heroUrl(WebApi网址)。AngularJS的网址是http://localhost:3000/,而WebApi的网址是http://localhost:5000/api/values。只要协议、域名、端口有任何一个不同,都被当作是不同的域。默认情况下是不支持直接跨域访问的。为了在WebiApi中增加跨域访问,我们要在WebApi的Startup.cs中打开Access-Control-Allow-Origin:

         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors(builder =>
{
builder.AllowAnyOrigin();
}); app.UseMvc();
}

现在程序访问正常。

我们查看AngularJS网站的network情况,在values的headers—Response Headers中有Access-Control-Allow-Origin: *的字样,这样就解决了前面出现的"No Access-Control-Allow-Origin header is present…"错误。

再继续测试,又出现问题了,这次是在更新时

错误信息如下:

我们都已经打开Access-Control-Allow-Origin,怎么还报错:"No Access-Control-Allow-Origin header is present…"?我们回过来看AngularJS,调用的是Update:

     updateHero(hero: Hero): Promise<Hero> {
const url = `${this.heroUrl}/${hero.id}`;
return this.http
.put(url, JSON.stringify(hero), { headers: this.headers })
.toPromise()
.then(() => hero)
.catch(this.handleError);
}

对应WebApi,这次调用的是Put方法

         [HttpPut("{id}")]
public void Put(int id, [FromBody]Hero hero)
{
Hero x = heroes.Single(h => h.Id == id); x.Name = hero.Name; UpdateHeroes(hero.Name);
}

是不是由于Method不对啊,以前都是Post、Get,这次是Put。趁早,一不做,二不休,在WebApi的Startup.cs中,写成如下:

         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors(builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
}); app.UseMvc();
}

允许所有Origin、Header和Method。这次彻底搞定,运行正常。

二、Update方法,必须使用类

其实上面Update时,还是遇到了部分问题的。就是按照最开始的程序,我们在WebApi的Put方法中,传入的是英雄名称name,而不是Hero对象。

         [HttpPut("{id}")]
public void Put(int id, [FromBody]string name)
{
Hero x = heroes.Single(h => h.Id == id); x.Name = name; UpdateHero(x);
}

同时AngularJS中如下:

     updateHero(hero: Hero): Promise<Hero> {
const url = `${this.heroUrl}/${hero.id}`;
return this.http
.put(url, JSON.stringify({name: name}), { headers: this.headers })
.toPromise()
.then(() => hero)
.catch(this.handleError);
}

在程序运行时,我们修改第13号英雄:

查看network,提交也是name: "Bombasto1111"。

然而,在WebApi中,调试情况下:

Id没有问题,但是name是null。[FromBody]要求前台传入的是Json串,然后提交到Action中。这时,Angular传入的是{name: "Bombasto1111"},通过FromBody转换后,是一个object,而不是一个string,因此name的值是null。

如果想用string name方式,AngularJS就要写成:

     updateHero(hero: Hero): Promise<Hero> {
const url = `${this.heroUrl}/${hero.id}`;
return this.http
.put(url, JSON.stringify(hero.name), { headers: this.headers })
.toPromise()
.then(() => hero)
.catch(this.handleError);
}

再看network

WebApi调试状态如下图,name有值,一切搞定。

也可以使用Hero作为参数的方式,如同我们文档最开始提供的代码一样。

     updateHero(hero: Hero): Promise<Hero> {
const url = `${this.heroUrl}/${hero.id}`;
return this.http
.put(url, JSON.stringify(hero), { headers: this.headers })
.toPromise()
.then(() => hero)
.catch(this.handleError);
}
        [HttpPut("{id}")]
public void Put(int id, [FromBody]Hero hero)
{
Hero x = heroes.Single(h => h.Id == id); x.Name = hero.Name; UpdateHeroes(x);
}

三、路由改变

我们在做Hero Search的时候,又要在后台WebApi中编写按照关键字查询的方法

         [HttpGet]
public IEnumerable<Hero> GetList(string key)
{
return heroes.Where(h => h.Name.Contains(key));
}

如果仅仅这样写,整个WebApi都不好用了。在调用Get时,出现了喜闻乐见的500错误。

而调用Get(int id)方法,就是localhost:5000/api/values/11时,一切正常。这是为什么呢?

因为按照微软的方式,如果有多个相同Method(例如HttpGet)的方法,WebApi路由就没法区分了。这里,Get()和GetList(string key)都是[HttpGet],路由都是localhost:5000/api/values。而Get(int id)方法由于写的是[HttpGet("{id}")],网址是localhost:5000/api/values/11,与Get()路由不同。因此,为了解决Get()和GetList(string key),我们在getlist方法上增加指定路由的方式。例如这里就是

         [HttpGet]
[Route("GetList")]
public IEnumerable<Hero> GetList(string key)
{
return heroes.Where(h => h.Name.Contains(key));
}

在AngularJS中,就可以使用localhost:5000/api/values/GetList?key=XXXX的方式调用,程序如下:

     search(term: string): Observable<Hero[]> {
return this.http.get(`${this.heroUrl}/GetList?key=${term}`).map((r: Response) => r.json() as Hero[]);
}

当然了,如果想更进一步,需要再修改整个路由方式,增加Action。这就需要在Controller上增加[Route("api/[controller]/[action]/")]的写法,并且将Action的[Route]删除。

[Route("api/[controller]/[action]/")]

public class ValuesController : Controller

这次是真的解决了。

AngularJS 2调用.net core WebAPI的几个坑的更多相关文章

  1. ajax 调用 .net core WebAPI,报错 400 (Bad Request) Unexpected character encountered while parsing value

    此文由博主前两天的提问及 dudu 的回答整理,地址:https://q.cnblogs.com/list/myquestion 情况说明 基于 .net core 写了一个 Web API,用 po ...

  2. Android调用 .Net Core WebApi 返回数据,用FastJSON解析一直报错。

    问题描述:.Net Core WebApi中用Newtonsoft.Json 把datatable转成json字符串,如:JsonConvert.SerializeObject(table,Forma ...

  3. .Net Core WebAPI 基于Task的同步&异步编程快速入门

    .Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以及基于Task的异步编程(asy ...

  4. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  5. Net Core WebAPI

    Net Core WebAPI .Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以 ...

  6. ASP.Net Core WebApi几种版本控制对比

    版本控制的好处: (1)助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方式进行控制,方法如下: (1)在 URL 中追加版本或 ...

  7. ASP.NET Core WebApi 返回统一格式参数(Json 中 Null 替换为空字符串)

    相关博文:ASP.NET Core WebApi 返回统一格式参数 业务场景: 统一返回格式参数中,如果包含 Null 值,调用方会不太好处理,需要替换为空字符串,示例: { "respon ...

  8. .net core webapi 前后端开发分离后的配置和部署

    背景:现在越来越多的企业都采用了在开发上前后端分离,前后端开发上的分离有很多种,那么今天,我来分享一下项目中得的前后端分离. B/S  Saas 项目:(这个项目可以理解成个人中心,当然不止这么点功能 ...

  9. Asp.Net Core WebApi中接入Swagger组件(初级)

    开发WebApi时通常需要为调用我们Api的客户端提供说明文档.Swagger便是为此而存在的,能够提供在线调用.调试的功能和API文档界面. 环境介绍:Asp.Net Core WebApi + S ...

随机推荐

  1. 布局神器:Flexbox

    最近的工作内容大多是移动端网页的开发,百分比布局,Media Queries,Bootstrap等常规的响应式/自适应的开发技术皆一一试过,但觉以上都不够灵活,所以,一直再尝试寻求更加灵活的精确的移动 ...

  2. C++ MPICH

    假设一个C++的MPI程序在单机上能够跑.可是在多机上跑会报下面错误: Fatal error in MPI_Send: Unkown error class , error stack. 解决方法: ...

  3. JMeter基础概念

    JMeter 介绍:一个非常优秀的开源的性能测试工具. 优点:你用着用着就会发现它的重多优点,当然不足点也会呈现出来. 从性能工具的原理划分: Jmeter工具和其他性能工具在原理上完全一致,工具包含 ...

  4. HDU1285——确定比赛名次

    Problem Description 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委 ...

  5. Android ActionBar完全解析,使用官方推荐的最佳导航栏(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/18234477 本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工 ...

  6. Android应用开发提高系列(4)——Android动态加载(上)——加载未安装APK中的类

    前言 近期做换肤功能,由于换肤程度较高,受限于平台本身,实现起来较复杂,暂时搁置了该功能,但也积累了一些经验,将分两篇文章来写这部分的内容,欢迎交流! 关键字:Android动态加载 声明 欢迎转载, ...

  7. Kerberos-KDC

    Kerberos提供一种较好的解决方案,它是由MIT发明的,Kerberos建立了一个安全的.可信任的密钥分发中心(KDC, Key Distribution Center).Kerberos是一种认 ...

  8. DIV+CSS 自适应布局

    两栏布局,左边定宽200px,右边自适应.如何实现?我的第一个反应就是:用flex伸缩盒呀,然后balabala...说完之后,面试官说,还有没有别的方法?flex有些浏览器不支持嗯...我愣了一下, ...

  9. 《第一行代码》学习笔记10-活动Activity(8)

    1.除了onRestart()方法,其他都是两两相对的.三种生存期: (1)完整生存期:onCreate()~onDestroy().一般情况下,一个活动会在onCreate()中完成各种初始化操作, ...

  10. Linux设置日期

    $ date -s "2016-07-13 14:54" 把时间设置为2016-07-13 14:54