nativeScript-apiclient
NativeScript 模块,用于简单调用基于HTTP的API。
npm i --save nativescript-apiclient

npm npm

NativeScript API客户端

一个NativeScript模块,用于简单调用基于HTTP的API。

Donate

NativeScript 工具箱

此模块是nativescript-toolbox的一部分。

许可证

MIT许可证

平台

  • Android
  • iOS

安装

在你的应用程序项目中运行以安装模块。

tns plugin add nativescript-apiclient

示例

快速入门

请查看plugin/index.ts或使用你的IDE的"IntelliSense"来了解其工作方式。

否则...

用法

导入

import ApiClient = require("nativescript-apiclient");

示例

import ApiClient = require("nativescript-apiclient");
import HTTP = require("http");

interface IUser {
displayName?: string;
id?: number;
name?: string;
}

var client = ApiClient.newClient({
baseUrl: "https://api.example.com/users",
route: "{id}",
});

client.beforeSend(function(opts: HTTP.HttpRequestOptions, tag: any) {
console.log("Loading user: " + tag);

// prepare the request here
})
.clientError(function(result: ApiClient.IApiClientResult) {
// handle all responses with status code 400 to 499
})
.serverError(function(result: ApiClient.IApiClientResult) {
// handle all responses with status code 500 to 599
})
.success(function(result: ApiClient.IApiClientResult) {
// handle all responses with that were NOT
// handled by 'clientError()' and 'serverError()'
//
// especially with status code less than 400 and greater than 599

var user = result.getJSON<IUser>();
})
.error(function(err: ApiClient.IApiClientError) {
// handle API client errors
})
.completed(function(ctx: ApiClient.IApiClientCompleteContext) {
// invoked after "result" and "error" actions
});

var credentials = new ApiClient.BasicAuth("Marcel", "p@ssword!");

for (var userId = 1; userId <= 100; userId++) {
// start a GET request
//
// [GET] https://api.example.com/users/{id}?ver=1.6.6.6
client.get({
authorizer: credentials,

// request headers
headers: {
'X-MyHeader-TM': '5979',
'X-MyHeader-MK': '23979'
},

// URL parameters
params: {
ver: '1.6.6.6'
},

// route parameters
routeParams: {
id: userId.toString() // {id}
},

// global value for all callbacks
tag: userId
});
}

路由

路由是基本URL的后缀。

您可以在该路由中定义一个或多个参数,这些参数在启动请求时将被替换。

如果您创建一个客户端如这样

var client = ApiClient.newClient({
baseUrl: "https://api.example.com/users",
route: "{id}/{resource}",
});

并启动一个请求如这样

client.get({
routeParams: {
id: "5979", // {id}
resource: "profile" // {resource}
}
});

客户端将调用以下URL

[GET]  https://api.example.com/users/5979/profile

参数值也可以是函数,这意味着该函数返回的值将用作值

var getUserId = function() : string {
// load the user ID from somewhere
};

client.get({
routeParams: {
id: getUserId, // {id}
resource: "profile" // {resource}
}
});

函数必须具有以下结构

function (paramName: string, routeParams: any, match: string, formatExpr: string, funcDepth: string) : any {
return <THE-VALUE-TO-USE>;
}
名称 描述
paramName 参数的名称。对于{id},这将变为id
routeParams 提交的参数列表及其值。重要:确保将值作为字符串返回!否则,您可能无法将值转换为URL部分。
match 参数的完整(未处理的)表达式。
formatExpr 参数的可选格式表达式。对于{id:number},这将变为number
funcDepth 此值最初为0。如果您在该函数中再次返回一个函数,此值将增加,直到您停止返回函数。

格式化值

跟随一个:字符,路由参数定义可以额外包含一个"格式表达式"。

这些表达式可以帮助您解析和格式化参数值。

为此,首先在客户端中定义一个所谓的"格式提供者"回调

client.addFormatProvider((ctx : ApiClient.IFormatProvider) => {
var toStringSafe = function() : string {
return ctx.value ? ctx.value.toString() : "";
};

if (ctx.expression === "upper") {
ctx.handled = true;
return toStringSafe().toUpperCase(); // the new value
}
else if (ctx.expression === "number") {
var n = parseInt(toStringSafe().trim())
;
if (isNaN(n)) {
throw "'" + ctx.value + "' is NOT a number!";
}

ctx.handled = true;
return n.toString();
}
});

在此,我们定义了两个表达式upper(转换为大写字母)和number(确保有一个有效的数字)。

要使用它们,您可以定义一个路由如下

{id:number}/{resource:upper}

现在,如果您设置客户端

var client = ApiClient.newClient({
baseUrl: "https://api.example.com/users",
route: "{id:number}/{resource:upper}",
});

并启动一个请求如这样

client.get({
routeParams: {
id: "5979",
resource: "profile"
}
});

客户端将调用以下URL

[GET]  https://api.example.com/users/5979/PROFILE

ctx对象在addFormatProvider()的格式提供者调用中具有以下结构

interface IFormatProviderContext {
/**
* Gets the format expression.
*/
expression: string;

/**
* Gets if the expression has been handled or not.
*/
handled: boolean;

/**
* Gets the underlying (unhandled) value.
*/
value: any;
}

授权

当您启动请求时,您可以提交一个可选的IAuthorizer对象

interface IAuthorizer {
/**
* Prepares a HTTP request for authorization.
*
* @param {HTTP.HttpRequestOptions} reqOpts The request options.
*/
prepare(reqOpts: HTTP.HttpRequestOptions);
}

插件提供以下实现

AggregateAuthorizer

var authorizer = new ApiClient.AggregateAuthorizer();
authorizer.addAuthorizers(new ApiClient.BasicAuth("Username", "Password"),
new ApiClient.BearerAuth("MySecretToken"));

BasicAuth

var authorizer = new ApiClient.BasicAuth("Username", "Password");

BearerAuth

var authorizer = new ApiClient.BearerAuth("MySecretToken");

OAuth

var authorizer = new ApiClient.OAuth("MySecretToken");
authorizer.setField('oauth_field1', 'field1_value');
authorizer.setField('oauth_field2', 'field2_value');

TwitterOAuth

var authorizer = new ApiClient.TwitterOAuth("<CONSUMER_KEY>", "<CONSUMER_SECRET>",
"<TOKEN>", "<TOKEN_SECRET>");

请求

GET

// ?TM=5979&MK=23979
client.get({
params: {
TM: '5979',
MK: '23979'
}
});

POST

client.post({
content: {
id: 5979,
name: "Tanja"
},

type: ApiClient.HttpRequestType.JSON
});

PUT

client.put({
content: '<user><id>23979</id><name>Marcel</name></user>',

type: ApiClient.HttpRequestType.XML
});

PATCH

client.patch({
content: '<user id="241279"><name>Julia</name></user>',

type: ApiClient.HttpRequestType.XML
});

DELETE

client.delete({
content: {
id: 221286
},

type: ApiClient.HttpRequestType.JSON
});

自定义

client.request("FOO", {
content: {
TM: 5979,
MK: 23979
},

type: ApiClient.HttpRequestType.JSON
});

日志记录

如果您想在结果/错误回调中记录日志,您必须在客户端中定义一个或多个日志操作

var client = ApiClient.newClient({
baseUrl: "https://example.com/users",
route: "{id}",
});

client.addLogger(function(msg : ApiClient.ILogMessage) {
console.log("[" + ApiClient.LogSource[msg.source] + "]: " + msg.message);
});

每个操作接收以下类型的一个对象

interface ILogMessage {
/**
* Gets the category.
*/
category: LogCategory;

/**
* Gets the message value.
*/
message: any;

/**
* Gets the priority.
*/
priority: LogPriority;

/**
* Gets the source.
*/
source: LogSource;

/**
* Gets the tag.
*/
tag: string;

/**
* Gets the timestamp.
*/
time: Date;
}

现在您可以开始记录回调中的登录操作

client.clientError(function(result : ApiClient.IApiClientResult) {
result.warn("Client error: " + result.code);
})
.serverError(function(result : ApiClient.IApiClientResult) {
result.err("Server error: " + result.code);
})
.success(function(result : ApiClient.IApiClientResult) {
result.info("Success: " + result.code);
})
.error(function(err : ApiClient.IApiClientError) {
result.crit("API CLIENT ERROR!: " + err.error);
})
.completed(function(ctx : ApiClient.IApiClientCompleteContext) {
result.dbg("Completed action invoked.");
});

使用 ILogger 接口的 IApiClientResultIApiClientErrorIApiClientCompleteContext 对象

interface ILogger {
/**
* Logs an alert message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
alert(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs a critical message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
crit(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs a debug message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
dbg(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs an emergency message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
emerg(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs an error message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
err(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs an info message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
info(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs a message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogCategory} [category] The optional log category. Default: LogCategory.Debug
* @param {LogPriority} [priority] The optional log priority.
*/
log(msg : any, tag?: string,
category?: LogCategory, priority?: LogPriority) : ILogger;

/**
* Logs a notice message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
note(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs a trace message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
trace(msg : any, tag?: string,
priority?: LogPriority) : ILogger;

/**
* Logs a warning message.
*
* @param any msg The message value.
* @param {String} [tag] The optional tag value.
* @param {LogPriority} [priority] The optional log priority.
*/
warn(msg : any, tag?: string,
priority?: LogPriority) : ILogger;
}

URL 参数

您可以为 URL 定义额外的参数。

如果您创建一个客户端如这样

var client = ApiClient.newClient({
baseUrl: "https://api.example.com/users"
});

并启动一个请求如这样

client.get({
params: {
id: '23979',
resource: "profile"
}
});

客户端将调用该 URL

[GET]  https://api.example.com/users?id=23979&resource=profile

类似于路由参数,您也可以使用函数来定义 URL 参数

var getUserId = function() : string {
// load the user ID from somewhere
};

client.get({
params: {
id: getUserId, // {id}
resource: "profile" // {resource}
}
});

函数必须具有以下结构

function (paramName: string, index: number, funcDepth: string) : any {
return <THE-VALUE-TO-USE>;
}
名称 描述
paramName 参数的名称。对于{id},这将变为id
索引 处理 URL 参数的零基于索引。
funcDepth 此值最初为0。如果您在该函数中再次返回一个函数,此值将增加,直到您停止返回函数。

重要提示:还建议将/返回字符串作为参数值,以防止在将值转换为 URL 字符串时出现问题。

响应

回调

简单

client.success(function(result : ApiClient.IApiClientResult) {
// handle any response
});

result 对象具有以下结构

interface IApiClientResult extends ILogger {
/**
* Gets the underlying API client.
*/
client: IApiClient;

/**
* Gets the HTTP response code.
*/
code: number;

/**
* Gets the raw content.
*/
content: any;

/**
* Gets the underlying (execution) context.
*/
context: ApiClientResultContext;

/**
* Gets the response headers.
*/
headers: HTTP.Headers;

/**
* Returns the content as wrapped AJAX result object.
*
* @return {IAjaxResult<TData>} The ajax result object.
*/
getAjaxResult<TData>() : IAjaxResult<TData>;

/**
* Returns the content as file.
*
* @param {String} [destFile] The custom path of the destination file.
*
* @return {FileSystem.File} The file.
*/
getFile(destFile?: string) : FileSystem.File;

/**
* Tries result the content as image source.
*/
getImage(): Promise<Image.ImageSource>;

/**
* Returns the content as JSON object.
*/
getJSON<T>() : T;

/**
* Returns the content as string.
*/
getString() : string;

/**
* Gets the information about the request.
*/
request: IHttpRequest;

/**
* Gets the raw response.
*/
response: HTTP.HttpResponse;
}

错误

client.error(function(err : ApiClient.IApiClientError) {
// handle an HTTP client error here
});

err 对象具有以下结构

interface IApiClientError extends ILogger {
/**
* Gets the underlying client.
*/
client: IApiClient;

/**
* Gets the context.
*/
context: ApiClientErrorContext;

/**
* Gets the error data.
*/
error: any;

/**
* Gets or sets if error has been handled or not.
*/
handled: boolean;

/**
* Gets the information about the request.
*/
request: IHttpRequest;
}

条件回调

您可以定义任何类型的条件的回调。

一个通用的方法是使用 if() 方法

client.if(function(result : IApiClientResult) : boolean {
// invoke if 'X-My-Custom-Header' is defined
return undefined !== result.headers["X-My-Custom-Header"];
},
function(result : IApiClientResult) {
// handle the response
});

如果没有条件匹配,则使用 success() 方法定义的回调。

对于特定的状态码,您可以使用 ifStatus() 方法

client.ifStatus((statusCode) => statusCode === 500,
function(result : IApiClientResult) {
// handle the internal server error
});

或简短

client.status(500,
function(result : IApiClientResult) {
// handle the internal server error
});
简写回调
client.clientError(function(result : ApiClient.IApiClientResult) {
// handle status codes between 400 and 499
});

client.ok(function(result : ApiClient.IApiClientResult) {
// handle status codes with 200, 204 or 205
});

client.serverError(function(result : ApiClient.IApiClientResult) {
// handle status codes between 500 and 599
});

以下方法也受支持

名称 描述
badGateway 处理状态码为 502 的请求。
badRequest 处理状态码为 400 的请求。
clientOrServerError 处理状态码在 400599 之间的请求。
conflict 处理状态码为 409 的请求。
forbidden 处理状态码为 403 的请求。
gatewayTimeout 处理状态码为 504 的请求。
gone 处理状态码为 410 的请求。
informational 处理状态码在 100199 之间的请求。
insufficientStorage 处理状态码为 507 的请求。
internalServerError 处理状态码为 500 的请求。
locked 处理状态码为 423 的请求。
methodNotAllowed 处理状态码为 405 的请求。
notFound 处理状态码为 404 的请求。
notImplemented 处理状态码为 501 的请求。
partialContent 处理状态码为 206 的请求。
payloadTooLarge 处理状态码为 413 的请求。
redirection 处理状态码在 300399 之间的请求。
serviceUnavailable 处理状态码为 503 的请求。
succeededRequest 处理状态码在 200299 之间的请求。
tooManyRequests 处理状态码为 429 的请求。
unauthorized 处理状态码为 401 的请求。
unsupportedMediaType 处理状态码为 415 的请求。
uriTooLong 处理状态码为 414 的请求。