You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Blazor中捕获RFID读卡器等外部设备的重复输入

Blazor对接HID模式RFID读卡器实现方案

核心思路

放弃依赖聚焦输入框接收输入的设计,直接在浏览器文档全局层面监听键盘输入流,利用模拟键盘输出的RFID读卡器的两个固有特征识别刷卡行为:

  • 单字符输入间隔极短(普遍<10ms,人工打字单字符间隔通常>80ms)
  • 输入完成后固定发送回车符作为结束标识
    该方案不需要页面放置任何可见输入控件,从根源规避焦点丢失、用户误修改输入、误切换焦点的问题。

具体实现

1. 前端监听逻辑(JS互操作层)

在wwwroot/js目录下新建rfidListener.js文件,实现全局键盘事件监听、输入缓存、刷卡判定逻辑:

let inputBuffer = [];
let lastInputTimestamp = 0;
// 输入间隔阈值,单位ms,可根据实际读卡器性能调整
const INPUT_INTERVAL_THRESHOLD = 30;
// 最小卡号长度,可根据自身使用的RFID卡规则调整
const MIN_CARD_LENGTH = 8;
let dotnetInstance = null;

export function initListener(dotnetRef) {
    dotnetInstance = dotnetRef;
    document.addEventListener('keydown', onKeyDownHandler);
}

function onKeyDownHandler(e) {
    const currentTimestamp = Date.now();
    // 输入间隔超过阈值,清空缓存,判定为人工输入起始
    if (currentTimestamp - lastInputTimestamp > INPUT_INTERVAL_THRESHOLD) {
        inputBuffer = [];
    }
    lastInputTimestamp = currentTimestamp;

    // 捕获回车符,判定为一次输入结束
    if (e.key === 'Enter') {
        if (inputBuffer.length >= MIN_CARD_LENGTH) {
            // 识别为有效刷卡,调用Blazor侧方法传递卡号
            dotnetInstance.invokeMethodAsync('OnCardScanned', inputBuffer.join(''));
        }
        inputBuffer = [];
        return;
    }

    // 仅缓存可打印字符,过滤Shift、Ctrl等功能键
    if (e.key.length === 1) {
        inputBuffer.push(e.key);
    }
}

export function disposeListener() {
    document.removeEventListener('keydown', onKeyDownHandler);
    dotnetInstance = null;
    inputBuffer = [];
}

2. Blazor页面侧实现

不需要添加任何<input>相关组件,在页面生命周期中初始化监听、销毁时释放资源即可:

@page "/rfid-scan"
@implements IAsyncDisposable
@inject IJSRuntime JSRuntime

@* 页面业务内容,可根据需求自定义 *@
<div class="page-container">
    <h3>刷卡操作区</h3>
    @if (!string.IsNullOrEmpty(CurrentCardNo))
    {
        <div class="alert alert-success mt-3">
            识别成功,卡号:@CurrentCardNo
        </div>
    }
</div>

@code {
    private IJSObjectReference? _rfidJsModule;
    private DotNetObjectReference<RfidScanPage>? _pageRef;
    private string? CurrentCardNo;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // 加载JS监听模块
            _rfidJsModule = await JSRuntime.InvokeAsync<IJSObjectReference>(
                "import", "./js/rfidListener.js");
            _pageRef = DotNetObjectReference.Create(this);
            // 启动全局监听
            await _rfidJsModule.InvokeVoidAsync("initListener", _pageRef);
        }
    }

    /// <summary>
    /// 前端JS识别到有效刷卡后调用的方法
    /// </summary>
    /// <param name="cardNo">读取到的RFID卡号</param>
    [JSInvokable]
    public Task OnCardScanned(string cardNo)
    {
        CurrentCardNo = cardNo;
        // 此处编写后续业务逻辑:卡号校验、关联数据查询、操作日志记录等
        // Blazor Server模式需手动调用StateHasChanged刷新UI
        StateHasChanged();
        return Task.CompletedTask;
    }

    public async ValueTask DisposeAsync()
    {
        if (_rfidJsModule != null)
        {
            await _rfidJsModule.InvokeVoidAsync("disposeListener");
            await _rfidJsModule.DisposeAsync();
        }
        _pageRef?.Dispose();
    }
}

方案优势

  • 无可见输入控件,不存在用户点击修改、切换焦点的入口,完全避免操作人员误操作
  • 不依赖任何元素的聚焦状态,只要当前浏览器页面处于激活状态,无论焦点在哪个元素上都能正常捕获刷卡输入,不存在失焦失效问题
  • 输入缓存和判定逻辑全部在前端完成,不会因为Blazor Server的SignalR传输延迟丢失字符
  • 自动区分人工键盘输入和读卡器输入,用户正常操作页面键盘输入不会被误识别为刷卡

参数调整说明

  • INPUT_INTERVAL_THRESHOLD:如果出现读卡器输入被截断的情况,可适当调大该值;如果出现人工快速打字被误识别,可适当调小该值,常规场景20~50ms区间均可稳定工作
  • MIN_CARD_LENGTH:根据所用RFID卡的实际卡号长度调整,进一步降低误触发概率
  • 若需要更高识别准确率,可额外增加总输入时长判定:从第一个字符输入到回车触发的总时长小于100ms才判定为有效刷卡

内容的提问来源于stack exchange,提问作者Avrohom Yisroel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:01:11