AngularJS 2调用.net core WebAPI的几个坑

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

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

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

WebApi

  View Code

 

AngularJS

  View Code

 

一、跨域访问(Cors)问题

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

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

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

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

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

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

复制代码
1         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
2         {
3             app.UseCors(builder =>
4             {
5                 builder.AllowAnyOrigin();
6             });
7 
8             app.UseMvc();
9         }
复制代码

 

现在程序访问正常。

我们查看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:

复制代码
1     updateHero(hero: Hero): Promise<Hero> {
2         const url = `${this.heroUrl}/${hero.id}`;
3         return this.http
4             .put(url, JSON.stringify(hero), { headers: this.headers })
5             .toPromise()
6             .then(() => hero)
7             .catch(this.handleError);
8     }
复制代码

 

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

复制代码
1         [HttpPut("{id}")]
2         public void Put(int id, [FromBody]Hero hero)
3         {
4             Hero x = heroes.Single(h => h.Id == id);
5 
6             x.Name = hero.Name;
7 
8             UpdateHeroes(hero.Name);
9         }
复制代码

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

复制代码
 1         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 2         {
 3             app.UseCors(builder =>
 4             {
 5                 builder.AllowAnyOrigin();
 6                 builder.AllowAnyHeader();
 7                 builder.AllowAnyMethod();
 8             });
 9 
10             app.UseMvc();
11         }
复制代码

 

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

二、Update方法,必须使用类

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

复制代码
1         [HttpPut("{id}")]
2         public void Put(int id, [FromBody]string name)
3         {
4             Hero x = heroes.Single(h => h.Id == id);
5 
6             x.Name = name;
7 
8             UpdateHero(x);
9         }
复制代码

同时AngularJS中如下:

复制代码
1     updateHero(hero: Hero): Promise<Hero> {
2         const url = `${this.heroUrl}/${hero.id}`;
3         return this.http
4             .put(url, JSON.stringify({name: name}), { headers: this.headers })
5             .toPromise()
6             .then(() => hero)
7             .catch(this.handleError);
8     }
复制代码

 

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

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

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

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

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

复制代码
1     updateHero(hero: Hero): Promise<Hero> {
2         const url = `${this.heroUrl}/${hero.id}`;
3         return this.http
4             .put(url, JSON.stringify(hero.name), { headers: this.headers })
5             .toPromise()
6             .then(() => hero)
7             .catch(this.handleError);
8     }
复制代码

 

再看network

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

 

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

复制代码
1     updateHero(hero: Hero): Promise<Hero> {
2         const url = `${this.heroUrl}/${hero.id}`;
3         return this.http
4             .put(url, JSON.stringify(hero), { headers: this.headers })
5             .toPromise()
6             .then(() => hero)
7             .catch(this.handleError);
8     }
复制代码

 

复制代码
        [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中编写按照关键字查询的方法

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

 

如果仅仅这样写,整个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方法上增加指定路由的方式。例如这里就是

复制代码
1         [HttpGet]
2         [Route("GetList")]
3         public IEnumerable<Hero> GetList(string key)
4         {
5             return heroes.Where(h => h.Name.Contains(key));
6         }
复制代码

 

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

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

 

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

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

public class ValuesController : Controller

 

这次是真的解决了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值