您的位置:首页 > Web前端 > AngularJS

ionic2+angular-in-memory-web-api(内置内存服务器)+跨域问题

2017-11-10 17:05 453 查看

随着angularJS内置服务器官网的一条更新,我想很多前端的工程师在本地进行调试时均出现了大问题。关于17-10-5号的改动我也纠结了许久,随着对于前端以及IONIC框架的学习,经验总结如下:



之前一直没有查询官方文档,对着AngularJS教程的HTTP服务部分代码直接复制粘贴。发现不论怎么操作,返回结果都为空。官方给出的原因在于返回的data数据不再压缩在data属性之中而是直接作为主体部分返回。那么意味着我们需要对源代码进行如下修改:

//gethero_service.ts
@Injectable()
export class HeroService {

//private headers = new Headers({'Content-Type': 'application/json'});
private heroesUrl = 'http://localhost:810/api/heroes';  // URL to web api

constructor(private http: Http) { }

getHeroes(): Observable<Hero[]> {
console.log(this.http.get(this.heroesUrl));
this.http.get(this.heroesUrl).subscribe((res:Response) =>{console.log(res.json())});
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
create(name: string): Observable<Hero> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });

return this.http.post(this.heroesUrl, { name }, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || { };  //主要修改的就是这一部分,之前return的是body.data由于合并之后,直接返回,所以不再需要调用内部属性;其余部分保持和官网一致。
}

private handleError (error: Response | any) {
// In a real world app, you might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}


在浏览器测试正常:



观察者设计模式

在各种服务实现过程中,用到的最重要的一个设计模式就是观察者设计模式。

public interface Subject{
public void registerObserver (Observer o);
public void removeObserver (Observer o);
public void notifyObservers();
}

public interface Observer{
public void update (float temp,float humidity,float pressure);
}

public interface DisplayElements{
public void display();
}


public class WeatherData implements Subject{
private ArrayList observers;
private float temp;
private float humidity;
private float pressure;

public WeatherData(){
observers = new ArrayList();
}
public void registerObserver(Observer o){
this.observers.add(o);
}
public void removeObserver (Observer o){
int i = this.observers.indexof(o);
this.observers.remove(i);
}
public void notifyObservers(){
for(int i=0,i<this.observers.size();i++){
Observer observer =(Observer)observers.get(i);
observer.update(float temp,float humidity,float pressure);
}
public void changes(){
this.notifyObservers();
}
}


public class CurrentConditionsDisplay implements Observer,DisplayElements{
private float temp;
private float humidity;
private float pressure;
private Subject weatherdata;

public CurrentConditionsDisplay(Subject weatherdata){
this.weatherdata = weatherdata;
weatherdata.registerObserver(this);//最重要的一部分
}
public void update(float temp,float humidity,float pressure){
this.temp=temp;
this.humidity=humidity;
this.pressure=pressure;
display();
}
public void dispaly(){
//(略)
}

}


修改之后官方测试用例在本地运行正常。这里需要补充官网对于Http各种服务两种写法的区别:

1、observable:可观察对象;是一个事件流。可以利用数组操作符对它进行处理,支持:请求——取消——新请求。

这里的结构有一条编程的黄金法则:总是把数据访问工作委托给一个支持性服务类。

Angular会把一个HeroService注入到组件的构造函数中,该组件将调用此服务来获取和保存数据。这个组件不会直接和 Angular 的 Http 客户端打交道! 它既不知道也不关心我们如何获取数据,这些都被委托给了HeroService去做。getHeroes()确实可以返回 HTTP 响应对象,但这不是最佳实践。 数据服务的重点在于,对消费者隐藏与服务器交互的细节。 调用HeroService的组件希望得到英雄数组。 它并不关心我们如何得到它们。 它也不在乎这些数据从哪里来。 毫无疑问,它也不希望直接和一个响应对象打交道。

在service.ts中HTTP 的 GET 方法被推迟执行。调用http.get仍然没有发送请求!这是因为可观察对象是 冷的, 调用subscribe()后,这个请求才会被发出。(Angular 的http服务把客户端/服务器通讯的工作委托给了一个叫做XHRBackend的辅助服务。)

getHeroes(): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
getHeroes() {
this.heroService.getHeroes()
.subscribe(
heroes => this.heroes = heroes,
error =>  this.errorMessage = <any>error);
}


2、promise:

遵循承诺的then(this.extractData).catch(this.handleError)模式。它是可观察对象的末端。我们不能在它上面调用map()函数或再次调用subscribe()函数。 Subscription对象的设计目的是不同的,这从它的主方法unsubscribe就能看出来。

//service.ts
getHeroes (): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
//html.ts
getHeroes() {
this.heroService.getHeroes()
.then(
heroes => this.heroes = heroes,
error =>  this.errorMessage = <any>error);
}


请求头 (headers)

我们通过Content-Type头{‘Content-Type’: ‘application/json’}告诉服务器,body 是 JSON 格式的。

接下来,使用headers对象来配置options对象。 options对象是RequestOptions的新实例,该类允许你在实例化请求时指定某些设置。这样, Headers 是 RequestOptions 中的一员。

跨域请求

出于安全的考虑,网络浏览器会阻止调用与当前页面不“同源”的远端服务器的XHR(http实际调用的get/post请求函数)。 所谓源就是 URI 的协议 (scheme)、主机名 (host) 和端口号 (port) 这几部分的组合。 这被称为同源策略。

跨域请求的两类服务器:

现代的CORS API和一个传统的JSONP搜索 API(只允许调用get方法)

//1. 等用户停止输入
//2. 当搜索关键字变化了才搜索
//3. 对付乱序响应体
this.items = this.searchTermStream
.debounceTime(300)
.distinctUntilChanged()
.switchMap((term: string) => this.wikipediaService.search(term));


跨站请求伪造攻击

在一个跨站请求伪造攻击(CSRF 或 XSRF)中,攻击者欺骗用户访问一个不同的网页,它带有恶意代码,秘密向你的应用程序服务器发送恶意请求。客户端和服务器必须合作来抵挡这种攻击。 Angular 的http客户端自动使用它默认的CookieXSRFStrategy来完成客户端的任务。

cookie(服务器生成,保存在客户端

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。


因为HTTP请求采用的是Ajax技术,其调用的核心代码模块为XMLHttpRequest();在请求头中,如果需要记录下client的cookie信息,需要在请求头中做如下修改:

let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers,withCredentials:true});//注意这里带了一个类似cookie的东西。
return this.http.post(this.heroesUrl,JSON.stringify({user_id:id,user_password:password}), options)
.map(this.extractData)
.catch(this.handleError);


withCredentials:true该属性是告诉浏览器,1、允许创建来自不同域的cookie信息;2、每次的跨域请求都允许带上该cookie信息。

IONIC2在浏览器中部署因为涉及到HTTP协议,故涉及到跨域问题,但当应用部署到手机上时,便采用file协议。故不存在跨域问题。本文解决跨域问题的思路是采用代理服务器,需要修改ionic.config.json文件的代理配置如下:

{
"name": "MenuDemo",
"app_id": "",
"type": "ionic-angular",
"integrations": {
"cordova": { }
},
"proxies": [{
"path": "/services",
"proxyUrl": "http://192.168.4.184:8080/DomainRIS/services"
}]
}




运行应用,上图中可见代理地址启动。跨域问题得到解决。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: