如何在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
相关产品推荐
相关产品推荐

