استفاده از سرویس جدید HTTP Client در Angular v4

HTTP client module یک تکنولوژی جدید است که کار با interface های backend برای HTTP آسان تر کرده است در این مقاله بطور خلاصه تنظیمات اولیه ی HttpClientModule را پوشش می دهیم، نحوه ی استفاده از متد های POST و GET را می گوییم و نحوه ی استفاده از ویژگی جدید interceptor را نشان می دهیم.

استفاده از سرویس جدید HTTP Client در Angular v4

با انتشار Angular v4.3 در ماه July برخی ویژگی های جدید معرفی شد که در بین آن ها HttpClientModule جدید، @angular/common/htt جایگزینangular/http @شد که کم حجم تر، ساده تر و کتابخانه ی بسیار قوی تری برای درخواست های HTTP دارد .

یک سرویس HttpClient جدید نیز در HttpClientModule گنجانده شده که می تواند برای شروع درخواست HTTP استفاده شود. در این آموزش نحوه استفاده از این client جدید را آموزش خواهیم داد و برخی از ویژگی های آن را بررسی خواهیم کرد.

TLDR

در ادامه تغییراتی که برای نسخه قدیمی pre-v4 به نسخه ی جدید v4+ نیاز است را آورده ایم.

برای وارد شدن به یک NgModule:

// below v4 ==========================================
import { HttpModule } from '@angular/http';

...
@NgModule({
    imports: [
        HttpModule
    ]
})
...

// v4+ ===============================================
import { HttpClientModule } from '@angular/common/http';

...
@NgModule({
    imports: [
        HttpClientModule
    ]
})
...

و برای استفاده از داخل یک سرویس:

// below v4 ==========================================
import { Http } from '@angular/http';

...
constructor(private http: Http) {}
...

// v4+ ===============================================
import { HttpClient } from '@angular/common/http';

...
constructor(private http: HttpClient) {}
...

حال بعد از نگاه سطحی اجازه دهید که یک نگاه عمیق تر به موضوع داشته باشیم.

نصب Angular v4

برای شروع نصب Angular CLI از Node و  اگر آن را از قبل نصب نکرده اید از npm استفاده کنید.

npm install -g @angular/cli@latest

سویچ  -g برای نصب کلی است و  @latest برای نصب آخرین نسخه است. پس از اتمام فرایند نصب دستور زیر را جهت شروع یک اپلیکیشن جدید اجرا کنید.

ng new httptutorial

با این کار قالب پروژه دانلود می شود و تمام وابستگی های آن نیز نصب می شود.ساختار دایرکتوری پروژه باید به شکل زیر باشد.

// end-to-end-tests
|- e2e/
  |----- app.e2e-spec.ts
  |----- app.po.ts
  |----- tsconfig.e2e.json

// npm dependencies
|- node_modules/

// public facing app. built things go here. this wont show until we run a build
|- dist/

// where most of the work will be done
|- src/
  |----- app/
      |----- app.component.css|html|spec.ts|ts
      |----- app.module.ts
  |----- assets/
  |----- environments/
      |----- environment.prod.ts|ts
  |----- favicon.ico
  |----- index.html
  |----- main.ts
  |----- polyfills.ts
  |----- styles.css
  |----- test.ts
  |----- tsconfig.app.json
  |----- tsconfig.spec.json
  |----- typings.d.ts

// overall configuration
|- .angular-cli.json  // the main configuration file
|- .editorconfig      // editorconfig which is used in some VS Code setups
|- .gitignore
|- karma.conf.js
|- package.json
|- protractor.conf.js
|- README.md
|- tsconfig.json
|- tslint.json

فایل package.json را باز کنید و وابستگی های angular را به نسخه ی 4.3.6 به روز رسانی کنید. بنابراین وابستگی ها و devDependenciessection فایل باید به شکل زیر باشد.

"dependencies": {
    "@angular/animations": "^4.3.6",
    "@angular/common": "^4.3.6",
    "@angular/compiler": "^4.3.6",
    "@angular/core": "^4.3.6",
    "@angular/forms": "^4.3.6",
    "@angular/http": "^4.3.6",
    "@angular/platform-browser": "^4.3.6",
    "@angular/platform-browser-dynamic": "^4.3.6",
    "@angular/router": "^4.3.6",
    "core-js": "^2.4.1",
    "rxjs": "^5.4.2",
    "zone.js": "^0.8.14"
  },
  "devDependencies": {
    "@angular/cli": "1.3.2",
    "@angular/compiler-cli": "^4.3.6",
    "@angular/language-service": "^4.3.6",
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "~3.1.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~3.2.0",
    "tslint": "~5.3.2",
    "typescript": "~2.3.3"
  }

سپس در دایرکتوری پروژه دستور زیر را اجرا کنید.

npm install

این کار در وابستگی های فایل package.json قرار می دهد. برای اینکه متوجه شوید آیا همه چیز به درستی کار میکند یا نه اجرای development web server را شروع کنید.

ng serve

این کار development web server را در http://localhost:4200 شروع می کند. این URL را چک کنید نهایتا چیزی مانند شکل زیر خواهید دید.

نصب HTTP module

در گام بعد در ماژول ریشه ی اپلیکیشن یعنی فایل  src/app/app.module.ts در لیست  imports ، HttpClientModule را وارد کنید بنابراین فایل باید به شکل زیر باشد:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

حال برای اینکه HttpClient در کامپوننت های شما استفاده شود نیاز است که در سازنده ی کلاس وارد شود در  src/app/app.component.ts ، HttpClient را وارد کنید  سپس آن را به سازنده مانند شکل زیر وارد کنید.

import { Component } from '@angular/core';

import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  constructor( private http: HttpClient ) { //dependency injection, creating an instance of HttpClient called http

  }
}

حال شما قادر خواهید بود عملیات  CRUD را انجام دهید و درخواست های HTTP را آزمایش کنید. متد های در دسترس HTTP متد های  post, put, delete, patch, head و jsonp است.

HTTP GET

برای تشخیص متد get یک API جعلی REST را وارد می کنیم. به http://jsonplaceholder.typicode.com/ بروید و تا قسمت Resources اسکرول کنید صفحه ای به شکل زیر خواهد دید:

سپس در زیر قسمت Resources روی /posts کلیک کنید

  دقت کنید که دسته ای از اشیاء json اینجا قرار دارد که هرکدام دارای چهار ویژگی هستند: userId, id, title و body بنابراین وقتی وارد URL ،  http://jsonplaceholder.typicode.com/posts می شویم از اپلیکیشن Angular ما نتیجه ی بالا را خواهیم گرفت. متد های دیگر HTTP به همین روش کار می کنند. حال  src/app/app.component.ts را ویرایش می کنیم تا به شکل زیر شود.

import { Component, OnInit } from '@angular/core'; // importing the OnInit interface

import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit { // implementing OnInit
  title = 'app';

  constructor( private http: HttpClient ) {

  }
  ngOnInit(): void { // adding the lifecycle hook ngOnInit
    this.http.get('http://jsonplaceholder.typicode.com/posts').subscribe(data => {
      console.log(data); // using the HttpClient instance, http to call the API then subscribe to the data and display to console
    });
  }

}

در اینجا ما نقطه پایانی API را در چرخه ی عمر ngOnInit فراخوانی می کنیم این کار را زمانی که کامپوننت تنظیم شده باشد انجام میدهیم. ابتدا interface مربوط به OnInit را وارد می کنیم سپس از این interface در تعریف کلاس استفاده می کنیم سپس متد ngOnInit در جایی که نمونه HttpClient را فراخوانی کرده ایم که بیش تر در سازنده ساخته ایم، فراخوانی می کنیم.

متد get را از این نمونه که انتظار می رود URL نقطه ی پایانی API موردنظر ما باشد فراخوانی می کنیم. متد get یک observable  برمی گرداند بنابراین نیاز است این observable را ثبت کنیم تا اطلاع رسانی شود. وقتی که پاسخ می رسد با فراخوانی متد subscribe به پایان می رسد. در متد subscribe بک event handler تعریف می کنیم که داده هایی که می توانند در کنسول چاپ شوند را می دهد خروجی که در کنسول مرورگر نشان داده می شود به شکل زیر باید باشد:

این ها اشیاء json هستند که از URL http://jsonplaceholder.typicode.com/posts هستند.

برای دستیابی به هر یک از ویژگی های شئ response برای مثال ویژگی data.userId نیاز داریم که شئ response را به نوعی که شامل ویژگی مرتبط باشد cast می کنیم. برای انجام این کار اجازه دهید که یک interface تعریف کنیم. دستورات زیر را  در فایل src/app/app.component.ts دقیقا بعد از کد های import وارد کنید .

interface DataResponse {
  userId: string;
  id: string;
  title: string;
}

سپس فراخوانی get را برای استفاده از interface مربوط به DataResponse ویرایش کنید.

this.http.get<DataResponse>('http://jsonplaceholder.typicode.com/posts/1').subscribe(data => {
      console.log('UserId: ' + data.userId);
      console.log('Id: ' + data.id);
      console.log('Title: ' + data.title);
      console.log('Body: ' + data.body);
    });

حال می توانیم به ویژگی userId ، title  و body  به صورت جداگانه دسترسی پیدا کنیم. خروجی در کنسول باید به شکل زیر باشد:

ممکن است بخواهیم یک پیغام خطا هنگامی که درخواست http ناموفق است نمایش دهیم. برای انجام این کار ابتدا HttpErrorResponse را از angular/common/http@ وارد می کنیم سپس یک تابع error handler با اضافه کردن یک callback به متد subscribe درست می کنیم سپس یک پارامتر از نوع HttpErrorResponse برای تابع error handler تعریف می کنیم :

import { HttpClient, HttpErrorResponse } from '@angular/common/http';

// ...

this.http.get<DataResponse>('https://jsonplaceholder.typicode.com/posts/1').subscribe(data => {
      console.log('UserId: ' + data.userId);
      console.log('Id: ' + data.id);
      console.log('Title: ' + data.title);
      console.log('Body: ' + data.body);
    },
    (err: HttpErrorResponse) => {
      if (err.error instanceof Error) {
        console.log('Client-side error occured.');
      } else {
        console.log('Server-side error occured.');
      }
    }
  );

HTTP post

دقیقا مانند قبل از سرویس JSONPlaceholder برای تحویل HTTP POST استفاده می کنیم. توجه کنید با این حال که این سرویس جعلی است داده ها دائم نیستند اما API ها مانند فراخوانی یک API ی واقعی پاسخ می دهند. نقطه ی پایانی درخواست POST ، http://jsonplaceholder.typicode.com/posts است . اگر این URL را ببینید خواهید دید که چهار ویژگی برای ما در دسترس است: userId, id, title و body. برای استفاده از این نقطه ی پایانی برای ساخت یک رکورد جدید یک فراخوانی دوم در چرخه ی عمر ngOnInit اضافه کنید:

this.http.post('http://jsonplaceholder.typicode.com/posts', {
      title: 'foo',
      body: 'bar',
      userId: 1
    })
      .subscribe(
        res => {
          console.log(res);
        },
        err => {
          console.log('Error occured');
        }
      );

متد POST نیز یک observable برمیگرداند بنابراین مانند قبل نیاز است که این observable را ثبت کنیم. این کار با فراخوانی متد subscribe به پایان می رسد در متد subscribe یک event handler که داده هایی که می توانیم در کنسول مرورگر چاپ کنیم را تعریف می کنیم سپس error handler رابرای چاپ اطلاعات هنگامی که یک خطا پیش می آید اضافه می کنیم. نتیجه ای که در کنسول مرورگر نمایش داده می شود باید به شکل زیر باشد:

HTTP Interceptors

  یک ویژگی جدید دیگر از HttpClientModule ، interceptor یا دریافت کننده ها هستند. یک interceptor بین اپلیکیشن و API مربوط به backend قرار می گیرد. با interceptor ها می توانیم درخواستی که از اپلیکیشن ما می آید را قبل از ارسال نهایی و فرستاده شدن به backend دستکاری کنیم عکس این موضوع نیز درست است که پاسخی است که از backend تولید می شود می تواند قبل از ارسال و پردازش توسط اپلیکیشن تغییر پیدا کند. برای نشان دادن این موضوع از header اطلاعاتی که از درخواست get به http://jsonplaceholder.typicode.com/posts/1 می آید  را جلوگیری می کنیم. بخش header که ما آن را تغییر خواهیم داد یک بخش Accept-Language است که از API می آید. فایل جدید src/app/typicode.interceptor.ts را بسازید و کد زیر را در آن وارد کنید:

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/observable';
@Injectable()
export class TypicodeInterceptor implements HttpInterceptor {
  intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const authReq = req.clone({
      headers: req.headers.set('Accept-Language', 'Test')
    });
    return next.handle(authReq);
  }
}

ابتدا injectable را از @angular/core وارد می کنیم سپس HttpEvent, HttpInterceptor, HttpHandler را از angular/common/http @  وارد می کنیم نهایتا پکیج Observable از rxjs/observable ارسال می شود.

سپسinjectable @ را اضافه می کنیم سپس کلاس TypicodeInterceptor را می سازیم که interface مربوط به HttpIntercepto را پیاده سازی می کند سپس متد interceptor را در پیاده سازی کلاس اضافه می کنیم.

این متد یک درخواست می گیرید و آن را قبل از پردازش های بیشتر در اپلیکیشن تغییر می دهد از آنجایی که دو پارامتر را به این متد می دهیم خود درخواست از نوع HttpRequest<any> و یک پارامتر که next نامیده می شود و از جنس HttpHandler است، می باشد. متد یک observable از نوع HttpEvent<any> برمی گرداند.

سپس متد req.clone() را برای شبیه سازی درخواست HTTP  اصلی فراخوانی می کنیم. داخل این متد بخش header را که به وسیله متد req.headers.set() ایجاد شده است را تغییر می دهیم. اینجا بخش Accept-Language را با تغییر مقدار آن برای Test تغییر می دهیم.

نهایتا شئ درخواست جدیدی که به تازگی ساخته شده (با header) برای پردازش های بیشتر با متد next.handle فرستاده می شود.

Interceptor Provider

برای اینکه interceptor برای اپلیکیشن ما کار کند نیاز داریم که فایل src/app/app.module.ts را تغییر دهیم.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { TypicodeInterceptor } from './typicode.interceptor';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: TypicodeInterceptor,
    multi: true
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }

اینجا HTTP_INTERCEPTORS را از angular/common/http @ وارد کرده ایم و کلاس TypicodeInterceptor که قبلا در src/app/typicode.interceptor.ts ساختیم سپس شئ تازه ساخته شده را به آرایه ای که در ویژگی @NgModule قرار دارد وارد می کنیم. این شئ دارای سه ویژگی است:

provide: باید برای HTTP_INTERCEPTORS تنظیم شود تا مشخص کند که آیا یک HTTP interceptor ارائه شده است یا نه.

useClass: باید با نوع کلاس interceptor ما تنظیم شود.

multi: نیاز است که با multi تنظیم شود تا به Angular بگوید که HTTP_INTERCEPTORS یک ارایه از مقادیر است نه یک مقدار واحد.

برای دیدن این interception در عمل روی زبانه ی network کلیک کنید صفحه را دوباره بارگذاری کنید سپس درخواست HTTP را در پنل سمت چپ انتخاب کنید درنتیجه header های HTTP در پنل سمت راست نمایش داده می شود. درخواستی که ما به دنبال آن هستیم درخواست get  است که آدرس http://jsonplaceholder.typicode.com/posts/1 را فراخوانی می کند.