Angular - 請求



Http 請求

在 Http 協議中,請求是指客戶端應用程式啟動與伺服器通訊的過程。在本章中,我們將瞭解如何在 Angular 框架中向伺服器傳送請求,以及請求階段可用的選項。

設定伺服器應用程式

為了實現 HTTP 客戶端-伺服器通訊,我們需要設定一個 Web 應用程式,並需要公開一組 Web API。可以從客戶端請求 Web API。讓我們建立一個示例伺服器應用程式 Expense API App,為費用提供 CRUD REST API。

步驟 1:轉到您喜歡的 workspace,如下所示:

cd /go/to/your/favorite/workspace

步驟 2:建立一個新資料夾 expense-rest-api 並移動到該資料夾中

mkdir expense-rest-api && cd expense-rest-api

步驟 3:使用 npm 命令提供的 init 子命令建立一個新應用程式,如下所示:

npm init

上述命令會詢問一些問題,並使用預設答案回答所有問題。

步驟 4:安裝 express 和 cors 包以建立基於節點的 Web 應用程式。

npm install express cors --save

步驟 5:安裝 sqlite 包以將費用儲存在基於 sqlite 的資料庫中

npm install sqlite3 --save

步驟 6:建立一個新檔案 sqlitedb.js 並新增以下程式碼以使用費用表和示例費用條目初始化資料庫。費用表將用於儲存費用專案

var sqlite3 = require('sqlite3').verbose()
const DBSOURCE = "expensedb.sqlite"

let db = new sqlite3.Database(DBSOURCE, (err) => {
   if (err) {
      console.error(err.message)
      throw err
   }else{
      console.log('Connected to the SQLite database.')
      db.run(`CREATE TABLE IF NOT EXISTS expense (
         id INTEGER PRIMARY KEY AUTOINCREMENT,
         item text, 
         amount real, 
         category text, 
         location text, 
         spendOn text, 
         createdOn text 
      )`,
         (err) => {
            if (err) {
               console.log(err);
            }else{
               var insert = 'INSERT INTO expense (item, amount, category, location, spendOn, createdOn) VALUES (?,?,?,?,?,?)'
         
               db.run(insert, ['Pizza', 10, 'Food', 'KFC', '2020-05-26 10:10', '2020-05-26 10:10'])
               db.run(insert, ['Pizza', 9, 'Food', 'Mcdonald', '2020-05-28 11:10', '2020-05-28 11:10'])
               db.run(insert, ['Pizza', 12, 'Food', 'Mcdonald', '2020-05-29 09:22', '2020-05-29 09:22'])
               db.run(insert, ['Pizza', 15, 'Food', 'KFC', '2020-06-06 16:18', '2020-06-06 16:18'])
               db.run(insert, ['Pizza', 14, 'Food', 'Mcdonald', '2020-06-01 18:14', '2020-05-01 18:14'])
            }
         }
      );  
   }
});

module.exports = db

步驟 7:開啟 index.js 並更新以下程式碼:

var express = require("express")
var cors = require('cors')
var db = require("./sqlitedb.js")

var app = express()
app.use(cors());

var bodyParser = require("body-parser");
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

var HTTP_PORT = 8000
app.listen(HTTP_PORT, () => {
   console.log("Server running on port %PORT%".replace("%PORT%", HTTP_PORT))
});

app.get("/", (req, res, next) => {
   res.json({ "message": "Ok" })
});

app.get("/api/expense", (req, res, next) => {
   var sql = "select * from expense"
   var params = []
   db.all(sql, params, (err, rows) => {
      if (err) {
         res.status(400).json({ "error": err.message });
         return;
      }
      res.json(rows)
   });
   
});

app.get("/api/jsonp/expense", (req, res, next) => {
   var sql = "select * from expense"
   var params = []
   db.all(sql, params, (err, rows) => {
      if (err) {
         res.status(400).json({ "error": err.message });
         return;
      }
      res.jsonp(rows)
   });

});

app.get("/api/expense/:id", (req, res, next) => {
   var sql = "select * from expense where id = ?"
   var params = [req.params.id]
   db.get(sql, params, (err, row) => {
      if (err) {
         res.status(400).json({ "error": err.message });
         return;
      }
      res.json(row)
   });
});

app.post("/api/expense/", (req, res, next) => {
   var errors = []
   if (!req.body.item) {
      errors.push("No item specified");
   }
   var data = {
      item: req.body.item,
      amount: req.body.amount,
      category: req.body.category,
      location: req.body.location,
      spendOn: req.body.spendOn,
      createdOn: req.body.createdOn,
   }
   var sql = 'INSERT INTO expense (item, amount, category, location, spendOn, createdOn) VALUES (?,?,?,?,?,?)'
   var params = [data.item, data.amount, data.category, data.location, data.spendOn, data.createdOn]
   db.run(sql, params, function (err, result) {
      if (err) {
         res.status(400).json({ "error": err.message })
         return;
      }
      data.id = this.lastID;
      res.json(data);
   });
})

app.post("/api/expense/:id/update_amount", (req, res, next) => {
   var errors = []
   if (!req.params.id) {
      errors.push("No item specified");
   }
   
   var sql = 'UPDATE expense SET amount = ? WHERE id = ?'
   var params = [req.body.amount, req.params.id]
   db.run(sql, params, function (err, result) {
      if (err) {
         res.status(400).json({ "error": err.message })
         return;
      }
      res.json({ id: req.params.id, amount: req.body.amount });
   });
})

app.put("/api/expense/:id", (req, res, next) => {
   if (req.params.id == null) {
      res.status(400).json({ "error": "Resource (Expense) Id is not send." })
      return
   }
   
   var data = {
      id: req.params.id,
      item: req.body.item,
      amount: req.body.amount,
      category: req.body.category,
      location: req.body.location,
      spendOn: req.body.spendOn
   }
   
   var sql = 'SELECT count(*) AS cnt FROM expense WHERE id = ?'
   var params = [data.id]
   db.get(sql, params, function (err, result) {
   if (err) {
      res.status(400).json({ "error": err.message })
      return;
   }
   
   if (result.cnt == 0) {
      var sql = 'INSERT INTO expense (id, item, amount, category, location, spendOn, createdOn) VALUES (?, ?,?,?,?,?,?)'
      var params = [data.id, data.item, data.amount, data.category, data.location, data.spendOn, data.createdOn]
      db.run(sql, params, function (err, result) {
         if (err) {
            res.status(400).json({ "error": err.message })
            return;
         }
         console.log(result)
         res.json(data);
      });
   } else {
      db.run(
         `UPDATE expense SET
            item = ?,    
            amount = ?,
            category = ?, 
            location = ?,    
            spendOn = ? 
            WHERE id = ?`,
         [data.item, data.amount, data.category, data.location, data.spendOn, data.id],
         function (err, result) {
            if (err) {
               console.log(err);
               res.status(400).json({ "error": res.message })
               return;
            }
            res.json(data)
         });
      }
   });

})

app.delete("/api/expense/:id", (req, res, next) => {
   db.run(
   'DELETE FROM expense WHERE id = ?',
   req.params.id,
   function (err, result) {
      if (err) {
         res.status(400).json({ "error": res.message })
         return;
      }
      res.json({ "message": "deleted", changes: this.changes })
   });
})

app.use(function (req, res) {
   res.status(404);
});

這裡,程式碼將建立以下六個提到的 REST API 端點

  • / 端點返回一個 OK 訊息以確保應用程式正常工作

  • /api/expense 端點返回資料庫中所有可用的費用專案

  • /api/jsonp/expense 端點以 jsonp 格式返回資料庫中所有可用的費用專案

  • /api/expense/:id 端點根據費用條目 ID 返回費用條目

  • /api/expense/:id 端點使用 put 方法將根據費用條目 ID 更新費用條目

  • /api/expense 端點使用 post 方法將新的費用條目新增到資料庫中

  • /api/expense/:id/update_amount 端點使用 post 方法將更新 URL 中指定的費用條目的金額

  • /api/expense/:id 端點使用 delete 方法將根據費用條目 ID 刪除費用條目

步驟 8:執行應用程式,如下所示:

node index.js

步驟 9:要測試應用程式並確保它正常工作,請開啟瀏覽器並轉到https://:8000/。如果應用程式正常工作,它應該返回以下訊息。

{ 
   "message": "Ok" 
}

設定 HttpClient 服務

在開始 http 請求之前,我們需要在應用程式中正確設定 HttpClient 服務。Angular 為 http 通訊提供了一個單獨的模組 HttpClientModule,其中包含 HttpClient 服務。要使用 HttpClient 服務,應將 HttpClientModule 匯入到應用程式中,無論是在應用程式的根/模組配置部分。

步驟 1:要在獨立應用程式(元件)中匯入 HttpClientModule,請在根配置中使用 importProvidersFrom 方法,如下所示:

import { bootstrapApplication } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { importProvidersFrom } from '@angular/core';

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

bootstrapApplication(AppComponent, {
   providers: [
      importProvidersFrom(HttpClientModule),
   ]
});

這裡,

importProvidersFrom 方法是從 angular core 包匯入的。它匯入 HttpClientModule 中包含的所有提供程式

步驟 2:要在 Web 應用程式中匯入 HttpClientModule,請在根/相應模組檔案中 imports 陣列中匯入 HttpClientModule 模組,如下所示:

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

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

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

匯入 http 模組將在 Angular 依賴注入設定中包含 HttpClient 服務,以便可以根據需要將 HttpClient 注入到任何 Angular 元件中,如下所示:

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

@Injectable()
export class MyService {
   constructor(private http: HttpClient) { }
}

請求/響應工作流程

HttpClient 提供了幾種方法來啟動 http 請求。基本上,所有方法都返回一個帶有可選型別變數的可觀察物件 (Observable<T>)。可以訂閱可觀察物件以啟動請求。一旦訂閱了可觀察物件,它就會啟動請求並將其傳遞給一系列已註冊的攔截器,最後到達伺服器。然後,它從伺服器接收響應並將其釋出到已訂閱的函式。

請求的工作流程如下:

  • 使用者建立一個新的 HttpClient 例項,例如透過元件的建構函式建立 http。

constructor(private http: HttpClient) { }
  • 使用者透過傳遞資源資訊來呼叫任何一個 HttpClient 方法,例如 request()。

let req = this.http.request(<action>, <url>, <body>, <option>)
  • request() 方法將使用給定的資源資訊建立一個可觀察物件,例如 req。

let req = this.http.request(<action>, <url>, <body>, <option>)
  • 使用者將使用 subscribe() 方法將回調函式訂閱到可觀察物件。

req.subscribe((data) => console.log(data)); 
  • 一旦使用者訂閱了 Observable,它就會將請求傳遞給已註冊的攔截器,其順序與攔截器的註冊順序相同。

  • 一旦請求透過所有已註冊的攔截器,Observable 將向伺服器傳送請求。

  • Observable 等待伺服器響應,一旦從伺服器接收到響應,它就會將響應返回給已訂閱的函式

  • 已訂閱的函式將執行必要的業務邏輯並將輸出設定為元件的變數。

  • 元件將使用 HttpClient 的輸出呈現其模板。

一個示例請求如下:

let req = this.http.request('GET', 'https://:8000/api/expense/1')
req.subscribe(data => this.myexpense = data); 

這裡,

  • this.http 是 HttpClient 例項

  • request() 是用於建立可觀察物件的方法,當訂閱時,它會啟動請求。

  • subscribe() 是用於訂閱返回的可觀察物件並隨後啟動請求和獲取響應的方法。

  • 最後,subscribe 方法的引數是用於獲取實際響應主體內容的回撥函式。

HttpClient 引數

HttpClient 方法支援的引數如下:

  • 資源 URI - 它基本上是表示資源的 URL/端點。

  • 請求正文 - 它基本上是與請求一起傳送到伺服器的資料。資料可以是查詢字串格式或 json 格式。

  • 請求選項 - 它基本上是與請求一起傳送的所有其他資料。它包含查詢字串、標題、cookie 等。

資源 URI

所有方法都接受 url/端點作為引數之一。它表示要從伺服器獲取的資源。在我們的示例應用程式中,url 以 https://:8000/ 開頭,一個可能的選項是 https://:8000/api/expenses。此端點將從伺服器獲取所有費用並以 json 格式將其傳送回客戶端。

請求正文

所有方法都接受 body 作為引數之一。body 引數在某些方法中是可選的。請求的正文將支援不同的格式,它將在請求的標頭 Content-Type 中使用 MIME 型別指定。最常見的資料型別如下:

  • 表單資料。MIME 型別為 application/x-www-form-urlencoded

  • 帶有上傳的表單資料。MIME 型別為 multipart/form-data

  • 純文字。MIME 型別為 text/plain

  • JSON。MIME 型別為 application/json

HttpParams 類

表單資料可以使用 Angular 提供的 HttpParams 類建立。HttpParams 以查詢字串格式接受資料(鍵/值作為 key=value,每個鍵/值用 & 分隔)。它具有將一個或多個引數作為序列新增的方法,方法是將方法連結在一起。所有方法都建立例項的副本,並在複製的例項中新增/刪除引數(鍵/值對),並將其返回。

HttpParams 建構函式的簽名如下:

constructor(options: HttpParamsOptions = {} as HttpParamsOptions)

其中,

HttpParamsOptions 物件可以透過以下屬性建立:

  • fromString?: string
  • fromObject?: {…}

使用建構函式建立 HttpParams 物件的示例程式碼如下:

/* using fromString option */
let httpParams = new HttpParams( { fromString: 'a=b*b=c' } )

/* using fromObject option */
let httpParams = new HttpParams( { fromObject: { a: "b", c: "d" } )

HttpParams 支援的方法如下:

  • set(): 接收一個引數和值。新增一個具有給定值的新引數。

  • delete(): 接收一個引數。刪除給定的引數。

  • has(): 接收一個引數。根據給定引數是否存在返回 true/false。

  • keys(): 不接收任何引數。返回所有引數。

  • get(): 接收一個引數。返回給定引數的第一個值。

  • getAll(): 接收一個引數。返回給定引數的所有值。

  • append(): 接收一個引數。將值新增到給定引數。

  • appendAll(): 接收一個包含多個引數的物件。將值新增到所有引數。

  • toString(): 不接收任何引數。以查詢字串格式返回物件。

建立 HttpParams 物件的示例程式碼如下:

let formData = new HttpParams();
formData = formData
   .set('item', 'Modified')
   .set('amount', 25);
console.log(formData.toString()) // item=Modified&amount=25

請求選項

無論使用何種方法,options 都是所有方法都接受的一個通用引數,用於表示請求選項。options 是一個包含標準請求資料的 JavaScript 物件(或雜湊表)。options 可以包含以下條目,並表示請求和響應的不同方面。

  • observe
  • responseType
  • headers
  • params
  • context
  • reportProgress
  • withCredentials
  • transferCache

讓我們在接下來的章節中逐一學習。

observe

observe 用於指定在伺服器通訊期間需要觀察響應的哪一部分,並將資料傳送回訂閱函式。根據 observe 選項,將返回完整的響應或部分響應。可能的值有 events、body 和 response。

events

events 用於返回伺服器響應流中觸發的事件。它將響應作為 Observable<HttpEvent> 型別返回。這裡,R 是要返回的實際資料(響應體)的型別。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'events', 
   responseType : 'json' 
});

這裡,

  • json 是用於解釋響應體的格式。

  • Expense 是用於轉換和返回響應體的型別。否則,它將以通用的 JavaScript 物件形式返回響應體。

response

response 用於返回伺服器的完整響應。它將響應作為 Observable<HttpResponse> 型別返回。這裡,R 是要返回的實際資料(響應體)的型別。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'response', 
   responseType : 'json' 
});

這裡,

  • json 是用於解釋響應體的格式。

  • expense 是用於轉換和返回響應體的型別。否則,它將以通用的 JavaScript 物件形式返回響應體。

body

body 用於僅返回伺服器響應的正文內容。它將響應作為 Observable 型別返回,這裡,R 是要返回的實際資料(響應體)的型別。

let req = this.http.request<Expense>( 'GET','https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json' 
});

這裡,

  • json 是用於解釋響應體的格式。

  • Expense 是用於轉換和返回響應體的型別。否則,它將以通用的 JavaScript 物件形式返回響應體。

responseType

responseType 用於解釋響應體。它可以具有以下四個可能的值:

  • arraybuffer
  • blob
  • text
  • json

讓我們逐一瞭解這些選項。

arraybuffer

arraybuffer 用於將響應體解釋為通用的原始二進位制資料緩衝區並返回 Observable。它可用於流式傳輸音訊/影片內容。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'arraybuffer' 
});

body

blob 用於將響應體解釋為二進位制格式並返回 Observable。它可用於下載大型檔案。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'blob' 
});

text

text 用於將響應體解釋為純文字格式並返回 Observable。它可用於表示基於文字的資料。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'text' 
});

json

json 用於將響應體解釋為 json 格式並返回 Observable,其中 R 是請求的資料型別 (Expense)。它可用於以 json 格式表示結果。透過在方法中指定型別變數 (R),可以將其進一步編碼為任何型別,如下所示:

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json' 
});

根據 observe 和 responseType,Httpclient 將返回具有不同型別變數的 Observable。讓我們檢查 observe 和 responseType 的一些組合以更好地理解這個概念。

  • observe => body 和 responseType => json

    返回 Observable。R 表示型別變數。

  • observe => response 和 responseType => json

    返回 Observable<HttpResponse>。R 表示型別變數並編碼響應體。

  • observe => events 和 responseType => json

    返回 Observable<HttpEvent>。R 表示型別變數並編碼響應體。

  • observe => events 和 responseType => arraybuffer

    返回 Observable<HttpEvent>。響應體被編碼為 ArrayBuffer。

  • observe => response 和 responseType => blob

    返回 Observable<HttpEvent>。響應體被編碼為 ArrayBuffer。

  • observe => response 和 responseType => text

    返回 Observable<HttpResponse>。響應體被編碼為 ArrayBuffer。

我們可以根據需要組合 observeresponseType 來建立更多組合。

headers

headers 用於指定 HTTP 標頭。它可以包含標準的 http 標頭作為鍵/值對,也可以使用 HttpHeaders 類對資料進行編碼。一個鍵/值對的示例標頭如下所示:

{ 'Content-type': 'application/json' }

它指定請求內容型別為 json。

Angular 提供了一個特殊的類 HttpHeaders 來組合標頭詳細資訊。它接受匿名物件作為標頭資訊並初始化它。它具有方法(set()append()delete() 等),可以透過將方法連結在一起,依次新增/刪除一個或多個標頭。所有方法都會建立例項的副本,並在副本中新增/刪除標頭資訊,並返回它。

let httpHeaders = new HttpHeaders( { 'Content-Type': 'application/json' }) 

httpHeaders = httpHeaders
   .set('Accept-Language', 'en-US,en;q=0.5')
   .set('Accept-Encoding', 'gzip, deflate, br');

let options = { 'headers': httpHeaders }

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', options );

req.subscribe( (data: Expense) => this.myexpense = data );

這裡,

  • httpHeaders 是一個用於封裝要傳送到伺服器的 HTTP 標頭資訊的 物件。它是使用 HttpHeaders 類建立的。

  • options 是一個包含標頭資訊的增強物件。

HttpHeaders 支援的方法如下:

  • set(): 接收一個標頭和值。新增一個具有給定值的新標頭。

  • delete(): 接收一個標頭。刪除給定的引數。

  • has(): 接收一個引數。根據給定標頭是否存在返回 true/false。

  • keys(): 不接收任何引數。返回所有引數。

  • get(): 接收一個引數。返回給定引數的第一個值。

  • getAll(): 接收一個標頭。返回給定引數的所有值。

  • append(): 接收一個引數。將值新增到給定標頭。

  • appendAll(): 接收一個包含多個引數的物件。將值新增到所有標頭。

  • toString(): 不接收任何引數。以查詢字串格式返回物件。

params

params 允許使用 HttpParams 類設定查詢字串。請檢視請求正文部分以瞭解有關 HttpParams 類的更多資訊。

建立 HttpParams 物件並在請求中設定它的示例程式碼如下:

let formData = new HttpParams();

formData = formData
   .set('item', 'Modified')
   .set('amount', 25);

console.log(formData.toString()) // item=Modified&amount=25

let req = this.http.request<Expense>('GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json',
   params: formData
});

context

context 用於以型別安全的方式並避免鍵衝突的方式傳送任意鍵/值對。它用作攔截器的資訊源,攔截器充當客戶端和伺服器之間的中介軟體。Angular 提供了一個特殊的類 HttpContext 來編碼上下文資訊。一個上下文示例如下所示:

// create a key using HttpContextToken
export const IS_AUTH_ENABLED = new HttpContextToken<boolean>(() => false);

// set data for the context
let authContext = new HttpContext().set(IS_AUTH_ENABLED, true)

let req = this.http.request<Expense>('GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json',
   context: authContext
});

這裡,

  • HttpContextToken 類用於建立上下文的鍵。它可以選擇指定值型別。

  • IS_AUTH_ENABLED 是鍵,其型別為布林值。

  • IS_AUTH_ENABLED 設定為 true。

我們可以使用 get() 方法透過傳遞令牌來獲取上下文值,如下所示:

let contextValue = req.context.get<boolean>(IS_AUTH_ENABLED) // true

reportProgress

reportProgress 用於指定是否從伺服器獲取請求(通訊)的進度。它可用於顯示透過 Web API 上傳大型檔案的進度。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'events', 
   responseType : 'json',
   reportProgress: true
});

withCredentials

withCredential 用於指定是否應將請求與傳出憑據(cookie)一起傳送。它接受布林值。

this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json' 
   withCredentials: true
});

transferCache

transferCache 用於指定是否應快取請求。它接受布林值或 HttpTransferCacheOptions 值。HttpTransferCacheOptions 用於編碼動態邏輯,以根據自定義篩選器函式篩選要快取的請求並覆蓋預設快取行為。

let req = this.http.request<Expense>( 'GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json', 
   transferCache: true
});

HttpClient 方法

HttpClient 類提供的用於客戶端-伺服器通訊的方法如下:

  • request()
  • head()
  • get()
  • post()
  • put()
  • patch()
  • delete()
  • jsonp()
  • options()

我們將在本章中學習通用 request() 方法,並在後續章節中學習其他方法。

request() 方法

request() 是一個通用方法,用於使用所有可能的 HTTP 動詞(如 get、post、patch 等)向伺服器傳送請求。它有許多過載。讓我們檢查兩個主要的過載函式,一個使用泛型選項,另一個使用 HttpRequest 物件。

  • 泛型選項 - 接收 url、http 動詞、正文內容和 options 物件。

  • HttpRequest 選項 - 接收 HttpRequest 物件。

HttpRequest 選項

Angular 提供了一個類 HttpRequest 來表示完整的 http 請求。它具有內建選項,可以包含 url、HTTP 方法/動詞、響應型別、標頭、引數等。

一個 HttpRequest 物件示例如下所示:

var httpRequest = new HttpRequest<Expense>('GET', 'https://:8000/api/expense/1', { 
   responseType : 'json' 
});

這裡,

  • this.http 是 HttpClient 例項。

  • GET 方法用作 HTTP 動詞。

  • 端點 (https:///api/expense/1) 將從伺服器返回 id 等於 1 的費用。

  • responseType 設定為 json。這將以 json 格式返回響應的正文。

上面的示例請求物件可以像下面這樣使用,以將請求傳送到伺服器:

var req = this.http.request<Expense>(httpRequest);

req.subscribe(data : HttpEvent<Expense> => { 
   this.myexpense = (data as HttpResponse<Expense>).body as Expense;
}

這裡,

  • request() 返回 HttpEvent,它是多種型別的聯合,如下所示:

type HttpEvent<T> = HttpSentEvent | HttpHeaderResponse | HttpResponse<T> | 
   HttpProgressEvent | HttpUserEvent<T>;
  • 由於我們將 responseType 設定為 json 並將 Expense 設定為要返回的物件,因此 observable 將在正文中返回 HttpResponse<Expense>。因此,我們將返回的資料物件轉換為 HttpResponse<Expense> 物件,然後從 body 屬性獲取費用。

泛型選項

它按給定順序接收四個引數,如下所示:

  • HTTP 方法/動詞
  • url
  • body(可選)
  • options

一個示例請求如下:

let req = this.http.request<Expense>('GET', 'https://:8000/api/expense/1', { 
   observe: 'body', 
   responseType : 'json',
});

req.subscribe(data => { this.myexpense = data });

工作示例

讓我們建立一個 Angular 工作示例,以使用 HttpClient 服務類和 HttpRequest 選項從伺服器獲取所有費用專案。

步驟 1:透過執行 ng new 命令建立一個新的 Angular 應用程式,如下所示:

ng new my-http-app

啟用 Angular 路由和 CSS,如下所示:

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS

步驟 2:透過在模組配置檔案 (app.module.ts) 中匯入 HttpClientModule 來啟用應用程式中的 http 通訊。

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

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

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

這裡,

  • 從 @angular/common/http 模組匯入 HttpClientModule。

  • 將 HttpClientModule 新增到 @NgModule 配置的 imports 部分。

步驟 3:建立一個新的介面 Expense 來表示我們的費用專案。

interface Expense {
   id: Number,
   item: String,
   amount: Number,
   category: String,
   location: String,
   spendOn: Date
}

export default Expense;

步驟 4:建立一個新的元件 ListExpenses 來顯示來自伺服器的費用專案。

ng generate component ListExpenses

它將建立如下所示的元件:

CREATE src/app/list-expenses/list-expenses.component.css (0 bytes)
CREATE src/app/list-expenses/list-expenses.component.html (28 bytes)
CREATE src/app/list-expenses/list-expenses.component.spec.ts (602 bytes)
CREATE src/app/list-expenses/list-expenses.component.ts (229 bytes)
UPDATE src/app/app.module.ts (581 bytes)

步驟 5:將我們的新元件包含到 App 根元件的檢視 app.component.html 中,如下所示:

<app-list-expenses></app-list-expenses>

<router-outlet></router-outlet>

步驟 6:透過建構函式將 HttpClient 注入到 ListExpenses 元件中,如下所示:

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

@Component({
   selector: 'app-list-expenses',
   templateUrl: './list-expenses.component.html',
   styleUrls: ['./list-expenses.component.css']
})
export class ListExpensesComponent {

   constructor(private http: HttpClient) { }
}

步驟 7:實現 OnInit 生命週期鉤子,以便在 ListExpenses 元件初始化後向伺服器請求費用。

export class ListExpensesComponent implements OnInit{

   constructor(private http: HttpClient) { }
   
   ngOnInit(): void {
   
   }
}

步驟 8:建立一個區域性變數 expenses 來儲存來自伺服器的費用。

export class ListExpensesComponent implements OnInit{

   expenses: Expense[] = [];
   
   constructor(private http: HttpClient) { }
   
   ngOnInit(): void {
   
   }
}

步驟9:建立一個HttpRequest物件並設定費用端點的URL。

export class ListExpensesComponent implements OnInit{

   expenses: Expense[] = [];
   
   constructor(private http: HttpClient) { }
   
   ngOnInit(): void {
   
      let req = new HttpRequest(
         'GET',
         'https://:8000/api/expense',{
            responseType: 'json'
         }
      )
   }
}

這裡,

  • GET設定為我們端點的HTTP方法

  • https://:8000/api/expense設定為我們端點的URL

  • json設定為responseType。這將把響應體解析為JSON格式。

步驟10:透過傳遞HttpRequest物件呼叫this.http(HttpClient例項)物件的request方法,並從伺服器獲取費用物件。然後,將費用設定到我們的本地變數expenses中。

export class ListExpensesComponent implements OnInit{

   expenses: Expense[] = [];
   
   constructor(private http: HttpClient) { }
   
   ngOnInit(): void {
   
   let req = new HttpRequest(
      'GET',
      'https://:8000/api/expense',{
         responseType: 'json'
      }
   )
   
   this.http.request<Expense[]>(req)
      .subscribe((data : HttpEvent<Expense[]> )=> {
         this.expenses = (data as HttpResponse<Expense[]>).body as Expense[]
         console.log(this.expenses)
      })
   }
}

這裡,

  • Expense[]設定為伺服器返回的物件的型別。伺服器將在其主體中以JSON格式傳送費用物件的陣列。

  • 訂閱請求(this.http.request)物件。然後將訂閱的資料解析為費用物件的陣列,並將其設定為本地費用變數(this.expenses

步驟11:ListExpensesComponent的完整程式碼如下:

import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpRequest, HttpResponse, HttpEvent } from '@angular/common/http';
import Expense from '../Expense';

@Component({
   selector: 'app-list-expenses',
   templateUrl: './list-expenses.component.html',
   styleUrls: ['./list-expenses.component.css']
})
export class ListExpensesComponent implements OnInit{

   expenses: Expense[] = [];
   
   constructor(private http: HttpClient) { }
   
   ngOnInit(): void {
   
      let req = new HttpRequest(
         'GET',
         'https://:8000/api/expense',{
            responseType: 'json'
         }
      )
      
      this.http.request<Expense[]>(req)
         .subscribe((data : HttpEvent<Expense[]> )=> {
            this.expenses = (data as HttpResponse<Expense[]>).body as Expense[]
            console.log(this.expenses)
         })
   }
}

步驟12:接下來,從元件中獲取費用物件,並在我們的元件模板頁面(list-expenses.component.html)中渲染它。

<div><h3>Expenses</h3></div>
<ul>
   <li *ngFor="let expense of expenses">
      {{expense.item}} @ {{expense.location}} for {{expense.amount}} USD on {{expense.spendOn | date:'shortDate' }}
   </li>
</ul>

步驟13:最後,使用以下命令執行應用程式:

ng serve

步驟14:開啟瀏覽器並導航到https://:4200/ URL,並檢查輸出。

template page

在這裡,輸出顯示我們的費用作為專案列表。

步驟15:讓我們透過將HttpRequest選項更改為通用選項來修改上述示例應用程式。

步驟16:按如下所示更改ngOnInit方法:

ngOnInit(): void {
   this.http.request<Expense[]>('GET', 'https://:8000/api/expense', {
      observe : 'body', 
      responseType : 'json'
   })
   .subscribe( data => {
      this.expenses = data as Expense[]
      console.log(this.expenses)
   }) 
}

這裡,

  • 刪除了HttpRequest物件

  • 透過合併通用選項更改了request方法的引數

  • 在第三個引數中設定observe選項為body。這將解析主體並僅返回主體,而不是整個響應。

  • 更改了subscribe()方法,並將返回的資料設定為費用的陣列(Expense[]

步驟17:執行應用程式並確認輸出。輸出與上述示例相同。

template page

結論

Angular 提供了一種透過HttpClientHttpRequest物件請求伺服器的簡單方法。request()方法是一個通用方法,用於支援所有HTTP動詞,如GET、POST、PUT、DELETE等。我們將在後續章節中學習更多針對特定HTTP動詞的方法。

廣告