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

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

ionic2+angular-in-memory-web-api(内置内存服务器)+跨域问题_第1张图片

之前一直没有查询官方文档,对着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 {
          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 {
        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);
      }
}

在浏览器测试正常:

ionic2+angular-in-memory-web-api(内置内存服务器)+跨域问题_第2张图片

观察者设计模式

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

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 {
    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 {
  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"  
  }]  
}

ionic2+angular-in-memory-web-api(内置内存服务器)+跨域问题_第3张图片
运行应用,上图中可见代理地址启动。跨域问题得到解决。

你可能感兴趣的:(网络)