跳转到内容

Web NFC:从网页读写 NFC 标签

发布于 更新于

一句话: 根据 MDN,“Web NFC API 允许通过轻量级的 NFC 数据交换格式(NDEF)消息在 NFC 上交换 数据”。MDN 将其标记为实验性,并指出它“不属于 Baseline,因为它在一些广泛使用的浏览器中尚不 可用”。

MDN 指出,“设备和标签必须经过专门格式化和写入,以支持 NDEF 记录格式才能与 Web NFC 一起使用”, 并且“该 API 目前不支持低层操作”(不过存在关于添加此类功能的公开讨论)。MDN 的 Web NFC API 概述页面列出了三个接口:NDEFMessage(“表示可以从兼容标签接收或发送到兼容 标签的 NDEF 消息”)、NDEFReader(“支持从兼容 NFC 标签读取和写入消息”)、NDEFRecord(“表示 可以包含在 NDEF 消息中的 NDEF 记录”)。W3C-CG 规范还额外定义了 NDEFReadingEvent 接口,用于下文 所述的 reading 事件。

const ndef = new NDEFReader();
await ndef.scan();
ndef.onreading = (event) => {
console.log(`读到 NFC 标签:${event.serialNumber}`);
};

根据 MDN,NDEFReader.scan() “激活一个读取设备,并返回一个 Promise,该 Promise 在 NFC 标签读取 操作被调度时 resolve,或在遇到硬件或权限错误时 reject”,并且“如果尚未获得 nfc 权限,会触发权限 提示”。NDEFReader.write() 在写入方面行为相同:它“尝试向标签写入一条 NDEF 消息”,同样会在尚未 获得授权时触发 nfc 权限提示。该接口会在“有新的读取结果可用于兼容 NFC 设备时”触发 reading 事件,并在“标签靠近读取设备但无法读取时”触发 readingerror 事件。

Web NFC 规范的安全策略明确规定:“只有安全上下文才被允许访问 NFC 内容”——“浏览器可以仅出于 开发目的忽略此规则。” 规范还将访问限制在前台:“Web NFC 功能仅允许用于顶层浏览上下文的 Document, 且其 visibilityState 为 'visible'”,并且“对于处于后台的网页,必须暂停 NFC 内容的接收与写入”。

规范将 Web NFC 定义为“一个默认的强大特性(powerful feature),由强大特性名称 nfc 标识”,其访问 需经过一个“获取权限”算法的把关,该算法会在允许访问前检查该特性当前的权限状态。

async function scanNfcTags(onTag) {
if (!("NDEFReader" in window)) {
// 此处不支持 Web NFC——改用手动输入或二维码作为回退方案。
return false;
}
const ndef = new NDEFReader();
await ndef.scan();
ndef.onreading = onTag;
return true;
}

根据 MDN 的浏览器兼容性数据,NDEFReader 的支持从 Chrome for Android 89 版本开始,MDN 数据中 WebView for Android 以及其他基于 Chromium 的 Android 浏览器均镜像(mirror)了与 Chrome for Android 相同的支持水平。桌面版 Chrome、Firefox、Safari 均报告不支持(version_added: false)。这与 Web NFC API 概述页面上 MDN 标注的“有限可用性”/“非 Baseline”标签一致。

  • 使用前先做 "NDEFReader" in window 特性检测——MDN 将 Web NFC 标记为实验性且非 Baseline, 根据 BCD 数据,其支持从 Chrome for Android 89+ 开始。
  • 通过 HTTPS 提供页面:规范规定“只有”安全上下文才被允许访问(并附有明确的仅限开发用途的 例外)。
  • 不要指望页面进入后台后读写仍会继续——规范要求在顶层文档不可见时暂停 NFC 访问。
  • 只要尚未获得 nfc 权限,调用 scan() 或 write() 就会出现权限提示(根据 MDN);处理 因硬件或权限错误而被拒绝的 Promise。
  • 阅读 MDN 的一般性说明——设备和标签“必须经过专门格式化和写入,以支持 NDEF 记录格式”才能 配合 Web NFC 使用——但注意规范在其基本交互示例中单独列出了“写入未格式化的 NFC 标签”, 因此不要假设每个标签在写入前都必须已经是 NDEF 格式。