- 版本:3.0.4
- GitHub: https://github.com/klippa-app/nativescript-http
- NPM: https://npmjs.net.cn/package/%40klippa%2Fnativescript-http
- 下载
- 前一天:30
- 上周:234
- 上个月:743
nativescript-http
:rocket: 在 NativeScript 中执行 HTTP 请求的最佳方式 :rocket
是核心 HTTP 的直接替换,具有重要的改进和新增功能,如适当的连接池、表单数据支持和证书固定。
功能
- 无需更改代码即可使用
- 可以使所有 HTTP 和图像缓存请求通过此插件进行
- 向下兼容(行为与核心 HTTP 相同)
- 现代 TLS & SSL 安全功能
- 共享连接池减少请求延迟
- 控制并发/连接池
- 静默恢复常见的连接问题
- 所有内容都在原生后台线程上运行
- 透明的 GZIP 以减小响应大小
- 支持 HTTP/2 和 SPDY
- 支持在内容属性中直接发布 ArrayBuffer/File/Blob/native 对象(如
java.io.File
和NSData.dataWithContentsOfFile
) - 支持多部分表单数据(multipart/form-data)(用于文件上传),支持 ArrayBuffer、File、Blob 和原生对象(如
java.io.File
和NSData.dataWithContentsOfFile
) - 可以设置全局用户代理
- 可以控制 Cookie
- 可以控制后台图像解析
- 证书/SSL 固定
- WebSocket
- 图像缓存
NativeScript 版本支持
NS 版本 | nativescript-http 版本 | 安装命令 | 文档 |
---|---|---|---|
^8.0.0 | ^3.0.0 | ns plugin add @klippa/nativescript-http@^3.0.0 | 此页面 |
^7.0.0 | ^2.0.0 | ns plugin add @klippa/nativescript-http@^2.0.0 | 此处 |
^6.0.0 | ^1.0.0 | tns plugin add @klippa/nativescript-http@^1.0.0 | 此处 |
安装(NS 8)
ns plugin add @klippa/nativescript-http@^3.0.0
使用(NS 8)
自动为此插件的所有 HTTP 调用使用
由于这是一个核心 HTTP 的直接替换,因此我们可以自动在 NativeScript 中使用此插件来执行所有使用 XHR 框架进行 HTTP 调用的 HTTP 调用,这包括
- 任何为在浏览器中使用而创建的 JavaScript/Angular/Vue 插件
- Axios
- Angular HTTPClient
- vue-resource
- 任何 NativeScript http 方法
- request
- fetch
- getString, getJSON, getImage, getFile, getBinary
- NativeScript image-cache
- 任何内部使用上述方法的 NativeScript 插件
完成此操作的方法非常简单,我们只需导入一个插件并将其添加到 webpack 配置中。
打开文件 webpack.config.js
,它可能看起来像这样
const webpack = require("@nativescript/webpack");
module.exports = (env) => {
webpack.init(env);
// Learn how to customize:
// https://docs.nativescript.cn/webpack
return webpack.resolveConfig();
};
导入我们的 webpack 实现,并在 webpack.resolveConfig()
之前添加一行,如下所示
const webpack = require("@nativescript/webpack");
const NativeScriptHTTPPlugin = require("@klippa/nativescript-http/webpack"); // Import NativeScriptHTTPPlugin
module.exports = (env) => {
webpack.init(env);
// Learn how to customize:
// https://docs.nativescript.cn/webpack
webpack.chainWebpack(config => {
config.plugin('NativeScriptHTTPPlugin').use(NativeScriptHTTPPlugin)
});
return webpack.resolveConfig();
};
NativeScriptHTTPPlugin
可以给定一个具有以下属性的对象:replaceHTTP
(true/false)和 replaceImageCache
(true/false)。这样,您可以控制插件替换的内容。如果您不提供此选项对象,我们将替换两者。选项可以按以下方式传递
webpack.chainWebpack(config => {
config.plugin('NativeScriptHTTPPlugin').use(NativeScriptHTTPPlugin, [
{
replaceHTTP: true,
replaceImageCache: false
}
])
});
注意:如果您这样做,则不需要执行其他集成。
通过添加自检来验证自动集成是否工作
如果您依赖于此插件的新功能,例如处理表单数据或证书固定,并且您想要确保自动集成始终工作,或者您只是想确保安全,您可以将以下自检添加到您的代码中
对于核心 NativeScript / Vue / Angular
import { Http, Dialogs } from "@nativescript/core";
Http.request({
method: "GET",
url: "https://nativescript-http-integration-check.local",
}).then((res) => {
const jsonContent = res.content.toJSON();
if (!jsonContent || !jsonContent.SelfCheck || jsonContent.SelfCheck !== "OK!") {
Dialogs.alert("nativescript-http automatic integration failed! Request to https://nativescript-http-integration-check.local failed");
}
}).catch((e) => {
Dialogs.alert("nativescript-http automatic integration failed! Request to https://nativescript-http-integration-check.local failed");
});
对于 Angular HttpClient
import { Dialogs } from "@nativescript/core";
// Don't forget to inject HttpClient into your component.
// Add the following code in a place where you want to do the self-check in Angular.
this.http.get("https://nativescript-http-integration-check.local", {
responseType: "json",
}).toPromise().then((res) => {
// @ts-ignore
if (!res || !res.SelfCheck || res.SelfCheck !== "OK!") {
Dialogs.alert("nativescript-http automatic integration failed! Request to https://nativescript-http-integration-check.local failed");
}
}).catch((e) => {
Dialogs.alert("nativescript-http automatic integration failed! Request to https://nativescript-http-integration-check.local failed");
});
此插件内部硬编码了 URL https://nativescript-http-integration-check.local
以始终返回相同的结果。
如果请求失败,或内容与预期不符,我们知道出了问题,并将收到一个对话框消息,表明自动集成失败。
代码中的集成
由于这是一个对 核心 HTTP 的直接替换,因此您可以使用与核心 HTTP 相同的方式执行请求,唯一不同的是导入方式
选项和请求输出的格式与核心 HTTP 相同。
import { HttpResponse } from "@nativescript/core";
import { Http } from "@klippa/nativescript-http";
Http.request({
url: "https://httpbin.org/get",
method: "GET"
}).then((response: HttpResponse) => {
// Argument (response) is HttpResponse
}, (e) => {
});
Angular 中的集成
我们还从 nativescript-angular
项目中提供了一个直接替换的 NativeScriptHttpClientModule
。
为了使 Angular 使用我们的 HTTP 实现,按如下方式导入我们的模块
import { NativeScriptHttpClientModule } from "@klippa/nativescript-http/angular";
@NgModule({
imports: [
NativeScriptHttpClientModule
]
从现在起,您可以使用 Angular 的 HttpClient 服务进行请求,如这里所述。
请注意,此插件会尝试在后台解析您的图像,因此您不需要在 JavaScript 中执行此操作(核心 HTTP 也这样做)。此值无法从 Angular HTTP 客户端访问,只能通过 response.content.toImage(),因此如果您打算下载图像并直接显示它们,建议您直接使用 HTTP 客户端(因此不使用 Angular HTTP 客户端)。
图像缓存
如果您使用 WebPack 插件,则无需执行任何操作即可使用我们的 ImageCache。它与核心的行为相同,因此您无需进行任何更改。
如果您不使用该插件。您可以从 @klippa/nativescript-http
中导入 ImageCache
类。它与核心 ImageCache 具有相同的 API。
关于支持 < Android 5 (SDK 21) 的应用的重要说明
NativeScript 的默认 minSdk 为 17,这是 Android 4.2。我们使用 OkHttp 版本 4,它 不支持 Android 4。
如果您不在乎 Android 4 用户
如果您不在乎 Android 4 用户,请编辑文件 App_Resources/Android/app.gradle
并将 minSdk 更改为 21
android {
defaultConfig {
minSdkVersion 21
// ... other config.
}
// ... other config.
}
这会让应用商店知道该应用无法安装在低于 Android 5 的设备上。
如果您在乎 Android 4 用户
幸运的是,OkHttp 有一个名为 okhttp_3.12.x 的特殊支持分支,用于较旧的 Android 版本,并且由于 OkHttp 是二进制安全的,这意味着所有方法都具有相同的签名,我们可以简单地替换版本,并且一切都会正常工作™。
我不介意使用较旧的 OkHttp 版本
如果您不介意每个人使用较旧的 OkHttp 版本,您可以进行以下简单™修复
编辑文件 App_Resources/Android/app.gradle
,添加以下行
android {
// ... other config.
configurations.all {
resolutionStrategy.force "com.squareup.okhttp3:okhttp:3.12.+"
}
}
这将强制您的构建使用 OkHttp 的支持版本。
请注意,此 okhttp_3.12.x 分支仅支持到 2020 年 12 月 31 日,并且它只会为严重错误或安全问题提供修复。
这意味着您将无法从版本 4 中获得任何酷炫功能。
我想为 Android 5 使用最新版本,为 Android 4 使用版本 3.12
注意:Android 运行时中目前有一个 开放问题,这使得以下配置无法工作
幸运的是,这也是一种可能性,但稍微困难一些,因为您必须拆分您的构建。
编辑文件 App_Resources/Android/app.gradle
,添加以下行
android {
// ... other config.
flavorDimensions "api"
productFlavors {
minApi21 {
dimension "api"
minSdkVersion 21
versionNameSuffix "-minApi21"
}
minApi17 {
dimension "api"
minSdkVersion 17
versionNameSuffix "-minApi17"
}
}
}
android.applicationVariants.all { variant ->
if (variant.name.contains("minApi17")) {
variant.getCompileConfiguration().resolutionStrategy.force "com.squareup.okhttp3:okhttp:3.12.+"
variant.getRuntimeConfiguration().resolutionStrategy.force "com.squareup.okhttp3:okhttp:3.12.+"
}
variant.outputs.each { output ->
if (variant.name.contains("minApi17")) {
output.versionCodeOverride = 10000000 + variant.versionCode
} else {
output.versionCodeOverride = 20000000 + variant.versionCode
}
}
}
android
部分是为了创建 2 个产品风味,一个用于 minSdk 17,另一个用于 minSdk 21。
android.applicationVariants
由两部分组成
- 确保flavor minApi17使用版本3.12.+的minSdk 17
- 确保每个flavor都有自己的构建版本号。它从清单中获取版本,并对minApi17使用(10000000 + manifestVersionCode),对minApi21使用(20000000 + manifestVersionCode)。
当您构建一个发布版本时,这将创建2个APK,一个用于Android 4(app-minApi17-release.apk),另一个用于Android 5(app-minApi21-release.apk)。您还可以将其与ABI拆分结合使用。
当您将这两个APK上传到Playstore时,Google会确保正确的APK被分发到不同的设备。
与其他NativeScript HTTP客户端的比较
插件 | Android | iOS | 后台线程 | 支持表单数据 | 适当的连接池 | 可以替换核心http | 证书/SSL固定 | WebSocket | 图像缓存 |
---|---|---|---|---|---|---|---|---|---|
@nativescript/core/http | :heavy_check_mark: 使用Java HttpURLConnection | :heavy_check_mark: 使用NSURLSession | :heavy_check_mark | :x | :x: Android实现不良 | - | :x | :x | - |
nativescript-background-http | :heavy_check_mark: 使用gotev/android-upload-service | :heavy_check_mark: 使用NSURLSession | :heavy_check_mark: (带服务) | :x | 未知 | :x | :x | :x | :x |
nativescript-http-formdata | :heavy_check_mark: 使用OkHttp4 | :heavy_check_mark: 使用OMGHTTPURLRQ | :x | :heavy_check_mark | :x: OkHttp实现不良 | :x | :x | :x | :x |
nativescript-okhttp | :heavy_check_mark: 使用OkHttp2 | :x | :x | :x | :x: OkHttp实现不良 | :x | :x | :x | :x |
nativescript-https | :heavy_check_mark: 使用OkHttp3 | :heavy_check_mark: 使用AFNetworking | :heavy_check_mark | :x | :heavy_check_mark: 共享客户端 | :white_check_mark: 通过手动替换调用,数据结构(几乎)相同 | :heavy_check_mark | :x | :x |
@klippa/nativescript-http | :heavy_check_mark: 使用OkHttp4 | :heavy_check_mark: 使用NSURLSession | :heavy_check_mark | :heavy_check_mark | :heavy_check_mark: 共享客户端 | :heavy_check_mark: 自动和手动 | :heavy_check_mark | :heavy_check_mark | :heavy_check_mark |
与NativeScript Core HTTP的实现差异
- 我们仅在Content-Type以
image/
开头时尝试将响应解析为Image - 我们使用默认的超时时间为60秒进行连接/写入/读取,您可以通过超时选项来更改它
- 虽然核心HTTP的代码看起来似乎支持FormData,但它只支持键值对,不支持文件,我们通过我们的
HTTPFormData
类支持它。
API
表单数据
默认情况下,此客户端对FormData对象的行为与核心HTTP相同,这意味着它将简单地将其编码为键=值对,并且不支持Blob/File对象。除非您使用自定义头覆盖它,否则它将作为application/x-www-form-urlencoded
发布。
如果您想创建多部分表单数据(multipart/form-data)请求,您可以使用此插件中的HTTPFormData类。您可以创建如下表单数据请求
import { HttpResponse } from "@nativescript/core";
import { Http, HTTPFormData, HTTPFormDataEntry } from "@klippa/nativescript-http";
const form = new HTTPFormData();
form.append("value", "Test");
// You can also append ArrayBuffer/File/Blob/native(such as java.io.File and NSData.dataWithContentsOfFile) objects directly to form here, but please keep in mind that only the File object has the ability to set a filename. And only Blob/File objects have the ability to set a content type.
// Use HTTPFormDataEntry if you want more control.
// formFile data can be a JavaScript ArrayBuffer but also native file objects like java.io.File and NSData.dataWithContentsOfFile.
const formFile = new HTTPFormDataEntry(new java.io.File(fileLocation), "test.png", "image/png");
form.append("file", formFile);
Http.request({
url: "https://httpbin.org/post",
method: "POST",
content: form
}).then((response: HttpResponse) => {
// Argument (response) is HttpResponse
}, (e) => {
});
注意:此功能不与Angular HTTPClient一起使用,因为它尝试将HTTPFormData转换为json。请使用request()方法进行多部分发布。
控制图像解码(仅限Android)
NativeScript HTTP实现始终尝试将响应解码为图像,以确保toImage()快速工作。然而,很多时候您不希望它这样做,因为您没有期望图像。默认情况下,此插件仅在端点返回正确的图像内容类型(ImageParseMethod.CONTENTTYPE
)时以此方式工作。使用此方法,您可以控制此行为,使用ImageParseMethod.ALWAYS
您将回退到核心HTTP行为,使用ImageParseMethod.NEVER
您可以完全禁用它。
注意:仅影响Android,在iOS上,图像解码仅在您使用toImage()时发生。
import { setImageParseMethod, ImageParseMethod } from "@klippa/nativescript-http";
// Add this line where you want to change the image parse mode.
// Options are: NEVER/CONTENTTYPE/ALWAYS.
setImageParseMethod(ImageParseMethod.ALWAYS);
控制cookie
清除所有cookie
import { clearCookies } from "@klippa/nativescript-http";
// Add this line where you want to clear cookies.
clearCookies();
控制并发/连接池限制
注意:仅域名限制对iOS有影响。
import { setConcurrencyLimits } from "@klippa/nativescript-http";
// Add this line where you want to set the concurrency limits.
// First argument is total limit, second per domain.
setConcurrencyLimits(20, 5);
设置全局User Agent
import { setUserAgent } from "@klippa/nativescript-http";
// Add this line where you want to set the user agent.
setUserAgent("MyCoolApp");
WebSocket
注意:证书固定在iOS上的WebSockets不可用。遗憾的是 SocketRocket移除了对此的支持。
在此插件中创建WebSocket相当简单
import { newWebsocketConnection } from "@klippa/nativescript-http/websocket";
newWebsocketConnection({
url: "wss://echo.websocket.org",
method: "GET",
}, {
// It's important to wrap callbacks in ngZone for Angular when you do anything binding related.
// If you don't do this, Angular won't update the views.
onClosed: (code: number, reason: string) => {
// Invoked when both peers have indicated that no more messages will be transmitted and the connection has been successfully released.
// No further calls to this callback will be made.
console.log("onClosed", code, reason);
},
onFailure: (error) => {
// Invoked when a web socket has been closed due to an error reading from or writing to the network.
// Both outgoing and incoming messages may have been lost. No further calls to this callback will be made.
console.log("onFailure", error);
},
onOpen: () => {
// Invoked when a web socket has been accepted by the remote peer and may begin transmitting messages.
console.log("onOpen");
},
onClosing: (code: number, reason: string) => {
// Invoked when the remote peer has indicated that no more incoming messages will be transmitted.
// This method will not be called on iOS.
console.log("onClosing", code, reason);
},
onMessage: (text: string) => {
// Invoked when a text (type 0x1) message has been received.
console.log("onMessage", text);
},
onBinaryMessage: (data: ArrayBuffer) => {
// Invoked when a binary (type 0x2) message has been received.
console.log("onBinaryMessage", data);
}
}).then((webSocket) => {
// With the webSocket object you can send messages and close the connection.
// Receiving a WebSocket here does not mean the connection worked, you have to check onFailure and onOpen for that.
});
证书固定
在启用之前,请阅读有关证书固定的信息。它可能具有严重的后果。好的文章在这里 这里 和 这里。
您可以通过这个Stack Overflow上的问题了解如何获取证书哈希值。
始终提供至少一个备份密钥
为了防止意外将用户锁定在您的应用之外,请确保您至少有一个备份密钥,并且有相应的程序在主要密钥无法使用时切换到备份密钥。例如,如果您将应用锁定到服务器证书的公钥,您应该在安全的地方生成一个备份密钥。如果您锁定到中间CA或根CA,那么您还应该选择一个您愿意在当前CA(或其中间CA)因某些原因无效时切换到的备用CA。
如果您没有备份密钥,您可能会无意中阻止您的应用工作,直到您发布新版本的应用,并且用户更新它。一次这样的事件导致一家银行不得不要求其CA使用已弃用的中间CA发行新证书,以便其用户可以使用该应用,否则应用将面临数周无法使用。
import { certificatePinningAdd, certificatePinningClear } from "@klippa/nativescript-http";
// Add this line where you want to pin the certificate to a specific domain. The second argument are the certificate hashes that you want to pin.
// You can use *.mydomain.com to also use this for direct subdomains, and **.mydomain.com for any subdomain.
// Note: for iOS, *.publicobject.com also behaves as **.publicobject.com due to limitation in TrustKit.
// Note 2: for Android, if you use the older version of OkHttp, the **. prefix does not work.
// Note 3: for iOS, you need to have at least 2 hashes, because Trustkit requires you to have a backup certificate.
certificatePinningAdd("mydomain.com", ["DCU5TkA8n3L8+QM7dyTjfRlxWibigF+1cxMzRhlJV4=", "Lh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=", "Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys="]);
// Use this to clear all certificate pins.
certificatePinningClear();
路线图
- 缓存控制
- 允许自签名证书(功能中的工作进度:feature/self-signed)
关于Klippa
Klippa是一家来自荷兰格罗宁根的快速成长公司,成立于2015年,由六位荷兰IT专家共同创立,旨在利用现代技术实现纸质流程的数字化。
我们通过使用机器学习和OCR帮助客户提高其组织的有效性。自2015年以来,我们已为1000多位满意的客户提供Klippa提供的各种软件解决方案。我们的热情是通过智能应用、应付账款软件和OCR数据提取来帮助客户实现纸质流程的数字化。
许可
MIT许可(MIT)