- Angular 教程
- Angular - 首页
- Angular - 概述
- Angular - 功能特性
- Angular - 优点与缺点
- Angular 基础
- Angular - 环境搭建
- Angular - 第一个应用
- Angular - MVC 架构
- Angular 组件
- Angular - 组件
- Angular - 组件生命周期
- Angular - 视图封装
- Angular - 组件交互
- Angular - 组件样式
- Angular - 嵌套组件
- Angular - 内容投影
- Angular - 动态组件
- Angular - 元素
- Angular 模板
- Angular - 模板
- Angular - 文本插值
- Angular - 模板语句
- Angular - 模板中的变量
- Angular - SVG 作为模板
- Angular 数据绑定
- Angular - 数据绑定及其类型
- Angular - 数据绑定
- Angular - 事件绑定
- Angular - 属性绑定
- Angular - 属性绑定
- Angular - 类和样式绑定
- Angular 指令
- Angular - 指令
- Angular - 内置指令
- Angular 管道
- Angular - 管道
- Angular - 使用管道转换数据
- Angular 依赖注入
- Angular - 依赖注入
- Angular HTTP 客户端编程
- Angular - 服务
- Angular - HTTP 客户端
- Angular - 请求
- Angular - 响应
- Angular - GET请求
- Angular - PUT请求
- Angular - DELETE请求
- Angular - JSON-P
- Angular - 使用 HTTP 进行 CRUD 操作
- Angular 路由
- Angular - 路由
- Angular - 导航
- Angular - Angular Material
- Angular 动画
- Angular - 动画
- Angular 表单
- Angular - 表单
- Angular - 表单验证
- Angular Service Workers 和 PWA
- Angular - Service Workers 和 PWA
- Angular 测试
- Angular - 测试概述
- Angular NgModule
- Angular - 模块介绍
- Angular 高级
- Angular - 身份验证和授权
- Angular - 国际化
- Angular - 可访问性
- Angular - Web Workers
- Angular - 服务器端渲染
- Angular - Ivy 编译器
- Angular - 使用 Bazel 构建
- Angular - 向后兼容性
- Angular - 响应式编程
- Angular - 指令和组件之间的数据共享
- Angular 工具
- Angular - CLI
- Angular 其他
- Angular - 第三方控件
- Angular - 配置
- Angular - 数据显示
- Angular - 装饰器和元数据
- Angular - 基本示例
- Angular - 错误处理
- Angular - 测试和项目构建
- Angular - 生命周期钩子
- Angular - 用户输入
- Angular - 最新动态?
- Angular 有用资源
- Angular - 快速指南
- Angular - 有用资源
- Angular - 讨论
Angular - PUT请求
HttpClient put() 方法
要创建/更新服务器上的资源,可以使用 HTTP 动词 put。put 的目的是将数据发送到服务器,并根据发送的数据在服务器上创建/更新资源。服务器将处理数据,并在服务器上创建或更新请求的资源。创建/更新数据后,响应将发送回客户端。
Angular HttpClient 提供了一个方法 put(),用于使用 put HTTP 动词将数据发送到服务器。让我们在本节学习 HttpClient put() 方法。
put() 方法的签名
put() 方法的签名如下:
URL 代表要请求的资源的 URI。
body 代表要发送到服务器的数据。通常,它将采用 JSON 格式。
options 代表要与资源 URL 一起发送的选项。
选项
可用的选项如下:
- observe
- responseType
- headers
- params
- context
- reportProgress
- withCredentials
- transferCache
observe
observe 用于指定在服务器通信期间要观察响应的哪一部分。根据 observe 选项,将返回 Observable 的完整响应或部分响应。可能的值为 body、events 和 response。
response 用于将来自 HTTP 请求的完整响应作为 Observable<HttpResponse<R>> 返回,其中 R 基于 requestType 选项(我们将在下一节中检查)和请求的数据类型 (Expense)。HttpResponse 类的目的是表示来自服务器的完整 HTTP 响应。
this.http.put<Expense>(<url>, { 'observe': 'response', 'responseType' : 'json' })
这里:
json 是用于解释响应体的格式
Expense 是用于格式化响应体并返回 Observable<HttpResponse<Expense>> 的请求类型。
events 用于将响应流中触发的事件以及相应的响应体作为 Observable<HttpEvent> 返回,其中 R 基于 requestType 选项和请求的数据类型 (Expense)。
this.http.put<Expense>(<url>, { 'observe': 'events', 'responseType' : 'json' })
这里:
json 是用于解释响应体的格式
Expense 是用于格式化响应体并返回 Observable<HttpEvent<Expense>> 的请求类型。
body 用于仅将 HTTP 请求的响应的正文内容作为 Observable 返回,其中 R 基于 requestType 选项和请求的数据类型 (Expense)。
this.http.put<Expense>(<url>, { 'observe': 'body', 'responseType' : 'json' })
这里:
json 是用于解释响应体的格式
Expense 是用于格式化响应体并作为 Observable<Expense> 返回的请求类型。
responseType
responseType 用于解释响应体。它可以有四个可能的值,如下所示:
- arraybuffer
- blob
- text
- json
让我们逐一了解这些选项
arraybuffer 用于将响应体解释为通用的原始二进制数据缓冲区并返回 `Observable`。它可用于流式传输音频/视频内容。
this.http.put(<url>, { 'observe': 'body', 'responseType' : 'arraybuffer' } )
blob 用于将响应体解释为二进制格式并返回 Observable<blob>。它可用于下载大型文件。
this.http.put(<url>, { 'observe': 'body', 'responseType' : 'blob' })
text 用于将响应体解释为纯文本格式并返回 Observable<String>。它可用于表示基于文本的数据。
this.http.put(<url>, { 'observe': 'body', 'responseType' : 'json' })
json 用于将响应体解释为 json 格式并返回 Observable<R>,其中 R 是请求的数据类型 (Expense)。它可用于以 json 格式表示结果。通过在方法中指定类型变量 (R),可以将其进一步编码为任何类型,如下所示:
this.http.put<Expense>(<url>, { '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。
-
我们可以根据需要组合 observe 和 responseType 来创建更多组合。
headers
headers 用于指定 HTTP 标头。它可以包含标准 HTTP 标头作为键值对,也可以在 HttpHeaders 类中编码数据。一个作为键值对的示例标头如下所示:
{ 'Content-type': 'application/json' }
它指定请求内容类型为 json。我们还可以使用 Angular 提供的 HttpHeaders 类来创建 HTTP 标头。使用 HttpHeaders 的示例标头信息如下:
// create header using `HttpHeaders` const headers = new HttpHeaders() .set('content-type', 'application/json') .set('Access-Control-Allow-Origin', '*'); this.http.put<Expense>(<url>, { 'observe': 'body', 'responseType' : 'json', headers: headers })
params
params 用于以 application/x-www-form-urlencoded 格式表示序列化请求参数。它可以包含参数作为键值对,也可以在 HttpParams 类中编码数据。一个作为键值对的示例参数如下所示:
{ 'name': 'john' }
它指定请求参数键为 name,其值为 john。我们还可以使用 Angular 提供的 HttpParams 类来创建参数。使用 HttpParams 的示例参数集如下:
// create parameters using `HttpParams` const params = new HttpParams() .set('name', 'john') .set('age', 25) .set('active', true; this.http.put<Expense>(<url>, { 'observe': 'body', 'responseType' : 'json', params: params })
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) this.http.request<Expense>('GET', <url>, { 'observe': 'body', 'responseType' : 'json', context: authContext })
这里:
HttpContextToken 用于创建键及其值类型。
IS_AUTH_ENABLED 是键,其类型为布尔值
reportProgress
reportProgress 用于指定是否应将请求(通信)的进度从服务器发送回客户端。它可用于显示通过 web api 上传大型文件的进度。
this.http.put<Expense>(<url>, { 'observe': 'events', 'responseType' : 'json', reportProgress: true })
withCredentials
withCredentials 用于指定是否应使用传出凭据(cookie)发送请求。它接受布尔值。
this.http.put<Expense>(<url>, { 'observe': 'body', 'responseType' : 'json', withCredentials: true })
transferCache
transferCache 用于指定是否应缓存请求。它接受布尔值或 HttpTransferCacheOptions 值。HttpTransferCacheOptions 用于编码动态逻辑,以根据自定义过滤器函数过滤要缓存的请求并覆盖默认缓存行为。
this.http.put<Expense>(<url>, { 'observe': 'body', 'responseType' : 'json', transferCache: true })
工作示例
为了进行 HTTP 客户端-服务器通信,我们需要设置一个 web 应用程序,并需要公开一组 web api。可以从客户端请求 web api。让我们创建一个示例服务器应用程序 Expense API App,为支出提供 CRUD REST API(主要是 PUT 请求)。
步骤 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/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.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.use(function (req, res) { res.status(404); });
这里,代码将创建以下六个 REST API 端点:
/endpoint 返回 OK 消息以确保应用程序正常工作
/api/expense 端点返回数据库中可用的所有支出项目
/api/expense/:id 端点根据支出条目 ID 返回支出条目
使用 put 动词的 /api/expense/:id 端点将根据支出条目 ID 更新支出条目
步骤 8:运行应用程序,如下所示:
node index.js
步骤 9:要测试应用程序并确保其正常工作,请打开浏览器并访问 https://127.0.0.1:8000/。如果应用程序正常工作,则应返回以下消息。
{ "message": "Ok" }
让我们创建一个有效的 Angular 示例,将资源放入上述服务器应用程序中,然后使用 HttpClient 服务类从服务器获取所有支出项目,包括新资源。
步骤 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;
这里:
`id` 设置为可选属性。
步骤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.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:创建一个局部变量 `expense` 来保存在服务器上创建的新支出。
export class ListExpensesComponent implements OnInit{ expenses: Expense[] = []; newexpense: Expense | null = null; constructor(private http: HttpClient) { } ngOnInit(): void { } }
步骤10:使用示例数据设置新的支出项,如下所示:
export class ListExpensesComponent implements OnInit { expenses: Expense[] = []; newexpense: Expense | null = null; constructor(private http: HttpClient) { } ngOnInit(): void { var spend_date = new Date(); spend_date.setDate(spend_date.getDate() - 1); this.newexpense = { 'item': 'new item ' + Math.floor(Math.random() * 10), 'amount': Math.floor(Math.random() * 100), 'category': 'Food', 'location': 'KFC', 'spendOn': spend_date } } }
这里:
使用随机方法设置项目名称和金额。
将支出日期设置为昨天。
步骤11:通过传递 `put` URL 和我们的新支出项来调用 `this.http`(`HttpClient` 实例)对象的 `put` 方法,并从服务器获取更新后的支出对象。
export class ListExpensesComponent implements OnInit { expenses: Expense[] = []; newexpense: Expense | null = null; constructor(private http: HttpClient) { } ngOnInit(): void { var spend_date = new Date(); spend_date.setDate(spend_date.getDate() - 1); this.newexpense = { 'item': 'new item ' + Math.floor(Math.random() * 10), 'amount': Math.floor(Math.random() * 100), 'category': 'Food', 'location': 'KFC', 'spendOn': spend_date } this.http.put<Expense>('https://127.0.0.1:8000/api/expense/1', this.newexpense,{ 'observe': 'body', 'responseType': 'json' }) .subscribe( data => { this.newexpense = data as Expense; console.log(data) }); } }
步骤12:通过传递支出列表 URL 和选项来调用 `this.http`(`HttpClient` 实例)对象的 `get` 方法,并从服务器获取支出对象。然后,将支出设置到我们的局部变量 `expenses` 中。
export class ListExpensesComponent implements OnInit { expenses: Expense[] = []; newexpense: Expense | null = null; constructor(private http: HttpClient) { } ngOnInit(): void { var spend_date = new Date(); spend_date.setDate(spend_date.getDate() - 1); this.newexpense = { 'item': 'new item ' + Math.floor(Math.random() * 10), 'amount': Math.floor(Math.random() * 100), 'category': 'Food', 'location': 'KFC', 'spendOn': spend_date } this.http.put<Expense>('https://127.0.0.1:8000/api/expense/1', this.newexpense,{ 'observe': 'body', 'responseType': 'json' }) .subscribe( data => { this.newexpense = data as Expense; console.log(data) }); this.http.get<Expense[]>('https://127.0.0.1:8000/api/expense',{ 'observe': 'body', 'responseType': 'json' }) .subscribe( data => { this.expenses = data as Expense[] console.log(this.expenses) }) } }
这里:
将 `Expense[]` 设置为服务器返回的对象的类型。服务器将在其正文中以 JSON 格式发送支出对象数组以及新的支出对象。
订阅请求(`this.http.get`)对象。然后将订阅的数据解析为支出对象的数组,并将其设置为局部支出变量(`this.expenses`)。
`ListExpensesComponent` 的完整代码如下:
import { Component, OnInit } from '@angular/core'; import { HttpClient, HttpRequest, HttpResponse, HttpEvent, HttpParams } 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[] = []; newexpense: Expense | null = null; constructor(private http: HttpClient) { } ngOnInit(): void { var spend_date = new Date(); spend_date.setDate(spend_date.getDate() - 1); this.newexpense = { 'item': 'new item ' + Math.floor(Math.random() * 10), 'amount': Math.floor(Math.random() * 100), 'category': 'Food', 'location': 'KFC', 'spendOn': spend_date } this.http.put<Expense>('https://127.0.0.1:8000/api/expense/1', this.newexpense,{ 'observe': 'body', 'responseType': 'json' }) .subscribe( data => { this.newexpense = data as Expense; console.log(data) }); this.http.get<Expense[]>('https://127.0.0.1:8000/api/expense',{ 'observe': 'body', 'responseType': 'json' }) .subscribe( data => { this.expenses = data as Expense[] console.log(this.expenses) }) } }
步骤14:接下来,从组件中获取支出列表对象和新的支出对象,并将其渲染到我们的组件模板页面(`list-expenses.component.html`)中。
<div>Expense item (id = 1) updated in the server is as follows:</div> <div>Item: {{newexpense?.item}}</div> <div>Amount: {{newexpense?.amount}}</div> <div>Location: {{newexpense?.location}}</div> <div>Spend On: {{newexpense?.spendOn | date:'short'}}</div> <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>
这里:
ID 为 1 的支出将在服务器上更新。
返回的支出将包含来自服务器的更新后的支出。
步骤15:最后,使用以下命令运行应用程序:
ng serve
步骤16:打开浏览器并导航到 `https://127.0.0.1:4200/` URL 并检查输出。
在这里,输出显示我们的支出作为一个项目列表。
结论
Angular 提供了一种简单的方法,可以通过 `HttpClient` 对象向服务器发送数据。`put()` 是用于向服务器发送数据的特定方法。我们将在接下来的章节中学习更多 HTTP 方法来针对其他 HTTP 动词。