1# 使用Node-API接口进行错误处理开发
2
3## 简介
4
5使用Node-API接口进行错误处理开发,使得在Node-API模块中能够更好地管理和响应错误情况。通过合理使用这些函数,可以提高模块的稳定性和可靠性。
6
7## 基本概念
8
9在ArkTS编程中,异常和错误是常见的概念。异常表示发生了某种意外情况,而错误则指示程序无法正确执行某些操作。Node-API提供了一系列方法来帮助开发者在Node-API模块中处理ArkTS中的异常和错误。下面是一些基本概念:
10
11- **异常(Exception)**:在程序执行过程中可能会出现的意外情况,可以是语法错误、运行时错误或逻辑错误,例如除以零或对未定义变量的操作。
12- **错误(Error)**:表示程序无法顺利执行某些操作,可以是由底层系统、API函数或开发者自定义的。
13- **类型错误(Type Error)**:表示操作或值的类型不符合预期的情况,通常是由于错误的数据类型导致的。
14- **范围错误(Range Error)**:表示一个值不在预期的范围内,例如对数组长度之外的索引进行访问。
15
16这些基本概念在异常和错误处理中非常重要,开发者需要通过适当的方法来捕获、处理或向用户报告这些异常和错误,以确保程序的稳定性和正确性。Node-API提供的方法可以帮助开发者在Node-API模块中处理ArkTS中的异常和错误。
17
18## 场景和功能介绍
19
20以下Node-API接口主要用于与ArkTS交互时处理错误和异常情况。他们的使用场景如下:
21| 接口 | 描述 |
22| -------- | -------- |
23| napi_create_error、napi_create_type_error、napi_create_range_error | 在C/C++中需要创建一个错误对象时,可以使用这些函数。创建的错误对象可以使用napi_throw抛出到ArkTS |
24| napi_throw | 当在C/C++中出现了错误或异常情况时,通过使用napi_create_error或napi_get_last_error_info方法创建或获取ArkTS Error对象,使用该方法抛出已有的ArkTS Error对象。 |
25| napi_throw_error、napi_throw_type_error、napi_throw_range_error | 当在C/C++中出现了错误或异常情况时,可以使用这些函数来抛出ArkTS中的异常。 |
26| napi_is_error | 检查一个napi_value是否代表一个错误对象时,可以使用这个函数。 |
27| napi_get_and_clear_last_exception | 当你需要获取最近一次出现的异常,并将异常队列清空时,可以使用这个函数。 |
28| napi_is_exception_pending | 当你需要判断是否有未处理的异常时,可以使用这个函数。 |
29| napi_fatal_error | 当遇到严重错误或不可恢复的情况时,可以使用这个函数引发致命错误来立即终止进程。 |
30| napi_fatal_exception | 抛出一个致命异常并终止进程, 同时产生相应的crash日志。|
31
32## 使用示例
33
34Node-API接口开发流程参考[使用Node-API实现跨语言交互开发流程](use-napi-process.md),本文仅对接口对应C++及ArkTS相关代码进行展示。
35
36### napi_get_last_error_info
37
38用于获取最后一次发生的错误信息,包括错误码、错误消息以及错误进栈信息,即使存在挂起的ArkTS异常,也可以调用此API。
39
40cpp部分代码
41
42```cpp
43#include "napi/native_api.h"
44#include <assert.h>
45static napi_value GetLastErrorInfo(napi_env env, napi_callback_info info)
46{
47    // 获取输入参数(这里以字符串message作为参数传入)
48    size_t argc = 1;
49    napi_value args[1] = {nullptr};
50    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
51    // 将传入的字符串参数以napi_get_value_int32取出,主动制造错误
52    int32_t value = 0;
53    napi_status status = napi_get_value_int32(env, args[0], &value);
54    // 接口使用错误,故返回值不为napi_ok
55    assert(status != napi_ok);
56    // 调用接口napi_get_last_error_info获取最后一次错误信息
57    const napi_extended_error_info *errorInfo;
58    napi_get_last_error_info(env, &errorInfo);
59    // 取出错误码与接口调用错误后其返回值作比较
60    assert(errorInfo->error_code == status);
61    // 取出错误消息作为返回值带出去打印
62    napi_value result = nullptr;
63    napi_create_string_utf8(env, errorInfo->error_message, NAPI_AUTO_LENGTH, &result);
64    return result;
65}
66```
67
68接口声明
69
70```ts
71// index.d.ts
72export const getLastErrorInfo: (str: string) => string;
73```
74
75ArkTS侧示例代码
76
77```ts
78import hilog from '@ohos.hilog'
79import testNapi from 'libentry.so'
80try {
81  hilog.info(0x0000, 'testTag', 'Test Node-API napi_get_last_error_info: %{public}s', testNapi.getLastErrorInfo('message'));
82} catch (error) {
83  hilog.error(0x0000, 'testTag', 'Test Node-API napi_get_last_error_info error: %{public}s', error);
84}
85```
86
87### napi_create_type_error
88
89创建并获取一个带文本信息的ArkTS TypeError。
90
91cpp部分代码
92
93```cpp
94#include "napi/native_api.h"
95
96static napi_value CreateTypeError(napi_env env, napi_callback_info info)
97{
98    // 构造errorCode和errorMessage
99    napi_value errorCode = nullptr;
100    napi_create_string_utf8(env, "napi_create_error errorCode", NAPI_AUTO_LENGTH, &errorCode);
101    napi_value errorMessage = nullptr;
102    napi_create_string_utf8(env, "napi_create_error errorMessage", NAPI_AUTO_LENGTH, &errorMessage);
103    // 调用napi_create_type_error创建一个typeError错误对象
104    napi_value error = nullptr;
105    napi_create_type_error(env, errorCode, errorMessage, &error);
106    return error;
107}
108```
109
110接口声明
111
112```ts
113// index.d.ts
114export const createTypeError: () => Error;
115```
116
117ArkTS侧示例代码
118
119```ts
120import hilog from '@ohos.hilog'
121import testNapi from 'libentry.so'
122try {
123  throw testNapi.createTypeError();
124} catch (error) {
125  hilog.error(0x0000, 'testTag', 'Test Node-API napi_create_type_error errorCode: %{public}s, errorMessage %{public}s', error.code, error.message);
126}
127```
128
129### napi_create_range_error
130
131创建并获取一个带文本信息的ArkTS RangeError。
132
133cpp部分代码
134
135```cpp
136#include "napi/native_api.h"
137
138static napi_value CreateRangeError(napi_env env, napi_callback_info info)
139{
140    // 构造errorCode和errorMessage
141    napi_value errorCode = nullptr;
142    napi_create_string_utf8(env, "napi_create_error errorCode", NAPI_AUTO_LENGTH, &errorCode);
143    napi_value errorMessage = nullptr;
144    napi_create_string_utf8(env, "napi_create_error errorMessage", NAPI_AUTO_LENGTH, &errorMessage);
145    // 调用napi_create_range_error创建一个typeError错误对象
146    napi_value error = nullptr;
147    napi_create_range_error(env, errorCode, errorMessage, &error);
148    return error;
149}
150```
151
152接口声明
153
154```ts
155// index.d.ts
156export const createRangeError: () => Error;
157```
158
159ArkTS侧示例代码
160
161```ts
162import hilog from '@ohos.hilog'
163import testNapi from 'libentry.so'
164try {
165  throw testNapi.createRangeError();
166} catch (error) {
167  hilog.error(0x0000, 'testTag', 'Test Node-API napi_create_range_error errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
168}
169```
170
171### napi_create_error
172
173创建并获取一个带文本信息的ArkTS Error。
174
175### napi_throw
176
177用于在Node-API模块中抛出ArkTS异常的函数。当在本机代码中发生错误或检测到不符合预期的情况时,可以使用此接口来抛出一个ArkTS异常,使其能够被捕获并处理。
178
179cpp部分代码
180
181```cpp
182#include "napi/native_api.h"
183
184static napi_value NapiThrow(napi_env env, napi_callback_info info)
185{
186    // 代码中发生某些错误后,可执行以下操作抛出异常
187    // 在Node-API环境中创建一个字符串,并将其存储在errorCode变量中
188    napi_value errorCode = nullptr;
189    napi_create_string_utf8(env, "throw errorCode", NAPI_AUTO_LENGTH, &errorCode);
190    // 在Node-API环境中创建一个字符串,并将其存储在errorMessage变量中
191    napi_value errorMessage = nullptr;
192    napi_create_string_utf8(env, "throw errorMessage", NAPI_AUTO_LENGTH, &errorMessage);
193    // 创建一个ArkTS对象error
194    napi_value error = nullptr;
195    napi_create_error(env, errorCode, errorMessage, &error);
196    // 通过napi_throw接口将对象抛出
197    napi_throw(env, error);
198    return nullptr;
199}
200```
201
202接口声明
203
204```ts
205// index.d.ts
206export const napiThrow: () => void;
207```
208
209ArkTS侧示例代码
210
211```ts
212import hilog from '@ohos.hilog'
213import testNapi from 'libentry.so'
214try {
215  testNapi.napiThrow();
216} catch (error) {
217  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
218}
219```
220
221### napi_throw_error
222
223用于抛出一个带文本信息的ArkTS Error。
224
225cpp部分代码
226
227```cpp
228#include "napi/native_api.h"
229
230// 这里直接抛出一个带有errorMessage的错误
231static napi_value NapiThrowErrorMessage(napi_env env, napi_callback_info info)
232{
233    napi_throw_error(env, nullptr, "napi_throw_error throwing an error");
234    return nullptr;
235}
236// 传入两个参数,在第二个参数,也就是除数为0的时候抛出一个错误
237static napi_value NapiThrowError(napi_env env, napi_callback_info info)
238{
239    // ArkTS侧传入两个参数
240    size_t argc = 2;
241    napi_value argv[2] = {nullptr};
242    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
243    // 将其转换为double类型的值作为被除数和除数
244    double dividend, divisor;
245    napi_get_value_double(env, argv[0], &dividend);
246    napi_get_value_double(env, argv[1], &divisor);
247    // 在这里判断除数如果为0则直接抛出一个错误,errorCode为:DIVIDE_BY_ZERO,errorMessage为:Cannot divide by zero
248    if (divisor == 0) {
249        napi_throw_error(env, "DIVIDE_BY_ZERO", "Cannot divide by zero");
250    }
251    return nullptr;
252}
253```
254
255接口声明
256
257```ts
258// index.d.ts
259export const napiThrowErrorMessage: () => void;
260export const napiThrowError: (dividend: number, divisor: number) => void;
261```
262
263ArkTS侧示例代码
264
265```ts
266import hilog from '@ohos.hilog'
267import testNapi from 'libentry.so'
268try {
269  testNapi.napiThrowErrorMessage();
270} catch (error) {
271  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw_error error code: %{public}s , message: %{public}s', error.code, error.message);
272}
273try {
274  testNapi.napiThrowError(5, 0);
275} catch (error) {
276  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw_error errorCode: %{public}s , errorManager: %{public}s', error.code, error.message);
277}
278```
279
280### napi_throw_type_error
281
282创建并获取一个带文本信息的ArkTS TypeError。
283
284cpp部分代码
285
286```cpp
287#include "napi/native_api.h"
288
289// 这里直接抛出一个带有errorMessage的TypeError
290static napi_value ThrowTypeErrorMessage(napi_env env, napi_callback_info info)
291{
292    napi_throw_type_error(env, nullptr, "napi_throw_type_error throwing an error");
293    return nullptr;
294}
295// 传入一个类型不匹配的参数,判断类型不匹配之后抛出typeError
296static napi_value ThrowTypeError(napi_env env, napi_callback_info info)
297{
298    // ArkTS侧传入一个参数
299    size_t argc = 1;
300    napi_value argv[1] = {nullptr};
301    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
302    // 将传入参数转换为napi_valuetype类型的值
303    napi_valuetype valueType;
304    napi_typeof(env, argv[0], &valueType);
305    // 如果传入参数不为napi_number类型的值则抛出TypeError
306    if (valueType != napi_number) {
307        // 这里抛出一个既带有errorCode也带有errorMessage的TypeError
308        napi_throw_type_error(env, "napi_throw_type_error", "Argument must be a number");
309    }
310    return nullptr;
311}
312```
313
314接口声明
315
316```ts
317// index.d.ts
318export const throwTypeErrorMessage: () => void;
319export const throwTypeError: (message: string) => void;
320```
321
322ArkTS侧示例代码
323
324```ts
325import hilog from '@ohos.hilog'
326import testNapi from 'libentry.so'
327try {
328  testNapi.throwTypeErrorMessage();
329} catch (error) {
330  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw_type_error errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
331}
332try {
333  testNapi.throwTypeError('str');
334} catch (error) {
335  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw_type_error errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
336}
337```
338
339### napi_throw_range_error
340
341创建并获取一个带文本信息的ArkTS RangeError。
342
343cpp部分代码
344
345```cpp
346#include "napi/native_api.h"
347
348// 这里直接抛出一个带有errorMessage的RangeError
349static napi_value ThrowRangeErrorMessage(napi_env env, napi_callback_info info)
350{
351    napi_throw_range_error(env, nullptr, "napi_throw_range_error one");
352    return nullptr;
353}
354// 传入不匹配的参数个数,判断不匹配之后抛出rangeError
355static napi_value ThrowRangeError(napi_env env, napi_callback_info info)
356{
357    // ArkTS侧传入两个参数
358    size_t argc = 2;
359    napi_value argv[2] = {nullptr};
360    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
361    // 如果传入参数个数不为2
362    if (argc != 2) {
363        // 这里抛出一个RangeError
364        napi_throw_range_error(env, "napi_throw_range_error", "Expected two numbers as arguments");
365        return nullptr;
366    }
367    // 下面将传入的两值相加并传出去
368    double numOne = 0;
369    double numTwo = 0;
370    napi_get_value_double(env, argv[0], &numOne);
371    napi_get_value_double(env, argv[1], &numTwo);
372    double result = numOne + numTwo;
373    napi_value resultValue;
374    napi_create_double(env, result, &resultValue);
375    return resultValue;
376}
377```
378
379接口声明
380
381```ts
382// index.d.ts
383export const throwRangeErrorMessage: () => void;
384export const throwRangeError: (num: number) => number | void;
385```
386
387ArkTS侧示例代码
388
389```ts
390import hilog from '@ohos.hilog'
391import testNapi from 'libentry.so'
392try {
393  testNapi.throwRangeErrorMessage();
394} catch (error) {
395  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw_range_error errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
396}
397
398try {
399  testNapi.throwRangeError(1);
400} catch (error) {
401  hilog.error(0x0000, 'testTag', 'Test Node-API napi_throw_range_error errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
402}
403```
404
405### napi_is_error
406
407用于判断给定的napi_value是否表示一个error对象。
408
409cpp部分代码
410
411```cpp
412#include "napi/native_api.h"
413
414static napi_value NapiIsError(napi_env env, napi_callback_info info)
415{
416    // 接收一个入参
417    size_t argc = 1;
418    napi_value args[1] = {nullptr};
419    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
420    // 调用接口napi_is_error判断入参是否为一个error对象
421    bool result = false;
422    // 如果napi_value为一个error对象,则设置result为true的布尔值,否则设置为false
423    napi_is_error(env, args[0], &result);
424    // 取出result通过napi_get_boolean接口将取出的bool值转换为napi_value类型的值返回出去
425    napi_value returnValue = nullptr;
426    napi_get_boolean(env, result, &returnValue);
427    return returnValue;
428}
429```
430
431接口声明
432
433```ts
434// index.d.ts
435export const napiIsError: <T>(obj: T) => boolean;
436```
437
438ArkTS侧示例代码
439
440```ts
441import hilog from '@ohos.hilog'
442import testNapi from 'libentry.so'
443try {
444  throw new Error("throwing an error");
445} catch (error) {
446  hilog.error(0x0000, 'testTag', 'Test Node-API napi_is_error error: %{public}s', testNapi.napiIsError(error)
447    .toString());
448  hilog.error(0x0000, 'testTag', 'Test Node-API napi_is_error error: %{public}s', testNapi.napiIsError(1)
449    .toString());
450}
451```
452
453### napi_get_and_clear_last_exception
454
455用于获取并清除最近一次出现的异常。
456
457cpp部分代码
458
459```cpp
460#include "napi/native_api.h"
461
462static napi_value GetAndClearLastException(napi_env env, napi_callback_info info)
463{
464    // 抛出异常,创造异常情况
465    napi_throw_error(env, "napi_create_error errorCode", "napi_create_error errorMessage");
466    // 调用napi_get_and_clear_last_exception接口获取并清除最后一个未处理的异常。即使存在挂起的ArkTS异常,也可以调用此API
467    napi_value result = nullptr;
468    napi_status status = napi_get_and_clear_last_exception(env, &result);
469    if (status != napi_ok) {
470        return nullptr;
471    }
472    return result;
473}
474```
475
476接口声明
477
478```ts
479// index.d.ts
480export const getAndClearLastException: () => Error | void;
481```
482
483ArkTS侧示例代码
484
485```ts
486import hilog from '@ohos.hilog'
487import testNapi from 'libentry.so'
488// 这里获取到最后一个未处理的异常
489hilog.info(0x0000, 'testTag', 'Test Node-API napi_get_and_clear_last_exception, error.message: %{public}s',
490           testNapi.getAndClearLastException());
491```
492
493### napi_is_exception_pending
494
495用于判断是否出现了异常。
496
497cpp部分代码
498
499```cpp
500#include "napi/native_api.h"
501
502static napi_value IsExceptionPending(napi_env env, napi_callback_info info)
503{
504    napi_status status;
505    bool isExceptionPending = false;
506    // 在执行一些可能引发异常的操作后
507    napi_throw_error(env, "napi_create_error errorCode", "napi_create_error errorMessage");
508    // 检查当前环境中是否有异常挂起
509    status = napi_is_exception_pending(env, &isExceptionPending);
510    if (status != napi_ok) {
511        return nullptr;
512    }
513    if (isExceptionPending) {
514        // 处理异常挂起的情况
515        napi_value result = nullptr;
516        status = napi_get_and_clear_last_exception(env, &result);
517        if (status != napi_ok) {
518            return nullptr;
519        }
520        // 将处理的异常返回出去
521        return result;
522    }
523    return nullptr;
524}
525```
526
527接口声明
528
529```ts
530// index.d.ts
531export const isExceptionPending: () => Object | void;
532```
533
534ArkTS侧示例代码
535
536```ts
537import hilog from '@ohos.hilog'
538import testNapi from 'libentry.so'
539interface MyObject {
540  code: string;
541  message: string;
542}
543try {
544  let result = testNapi.isExceptionPending() as MyObject;
545  hilog.info(0x0000, 'testTag', 'Test Node-API napi_is_exception_pending, error.Code: %{public}s, error.message: %{public}s',
546    result.code, result.message);
547} catch (error) {
548  hilog.error(0x0000, 'testTag', 'Test Node-API napi_is_exception_pending error');
549}
550```
551
552### napi_fatal_error
553
554用于引发致命错误以立即终止进程。在调用napi_fatal_error函数后,导致应用程序终止,因此应该慎重使用,避免在正常操作中频繁调用该函数。
555
556cpp部分代码
557
558```cpp
559#include "napi/native_api.h"
560
561static napi_value FatalError(napi_env env, napi_callback_info info)
562{
563    // 请注意,使用napi_fatal_error函数会导致应用进程直接终止,因此应该谨慎使用,仅在遇到无法恢复的严重错误时才应该调用该函数
564    // 模拟一个致命错误条件
565    bool errorCondition = true;
566    if (errorCondition) {
567        // 创建一个致命错误信息
568        napi_fatal_error("napi_fatal_error test", NAPI_AUTO_LENGTH, "napi_create_error errorMessage", NAPI_AUTO_LENGTH);
569    }
570    return nullptr;
571}
572```
573
574接口声明
575
576```ts
577// index.d.ts
578export const fatalError: () => void;
579```
580
581ArkTS侧示例代码
582
583```ts
584import hilog from '@ohos.hilog'
585import testNapi from 'libentry.so'
586try {
587  testNapi.fatalError();
588} catch (error) {
589  hilog.error(0x0000, 'testTag', 'Test Node-API napi_fatal_error error');
590}
591```
592
593### napi_fatal_exception
594在主线程的上下文环境中调用napi_fatal_exception函数后,抛出一个致命异常,导致应用程序终止,同时会生成相应的crash日志。因此应该慎重使用,避免在正常操作中频繁调用该函数。
595
596cpp部分代码
597
598```cpp
599#include "napi/native_api.h"
600
601static napi_value FatalException(napi_env env, napi_callback_info info)
602{
603    size_t argc = 1;
604    napi_value args[1] = {nullptr};
605
606    napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
607    if (status != napi_ok) {
608      return nullptr;
609    }
610    // 请注意,使用napi_fatal_exception函数会导致应用进程直接终止,因此应该谨慎使用,仅在主线程遇到无法恢复的严重错误时才应该调用该函数
611    // 模拟一个致命错误条件
612    status = napi_fatal_exception(env, args[0]);
613    if (status != napi_ok) {
614      return nullptr;
615    }
616    return nullptr;
617}
618```
619
620接口声明
621
622```ts
623// index.d.ts
624export const fatalException: (err: Error) => void;
625```
626
627ArkTS侧示例代码
628
629```ts
630import hilog from '@ohos.hilog'
631import testNapi from 'libentry.so'
632
633const err = new Error("a fatal exception occurred");
634testNapi.fatalException(err);
635```
636
637以上代码如果要在native cpp中打印日志,需在CMakeLists.txt文件中添加以下配置信息(并添加头文件:#include "hilog/log.h"):
638
639```text
640// CMakeLists.txt
641add_definitions( "-DLOG_DOMAIN=0xd0d0" )
642add_definitions( "-DLOG_TAG=\"testTag\"" )
643target_link_libraries(entry PUBLIC libhilog_ndk.z.so)
644```