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

.NET Core SignalR中验证应置于何处?如何传递请求验证错误?

Handling Validation Errors in .NET Core SignalR Hub Methods

Great question—this is a common pain point when migrating from full Framework SignalR to .NET Core, since the old HubPipelineModule approach no longer exists. Let's break down your options, including both pipeline-level and method-level solutions, and how to return structured error sets.


1. Use a Custom HubFilter (Pipeline-Level Validation)

.NET Core SignalR introduced Hub Filters as the replacement for HubPipelineModule—they let you intercept method calls globally, which is perfect for cross-cutting concerns like validation. Here's how to implement it:

Step 1: Create a Validation Hub Filter

You can use data annotations (or any validation library) to validate incoming parameters, then throw a structured error if validation fails:

using Microsoft.AspNetCore.SignalR;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Collections.Generic;

public class ValidationHubFilter : IHubFilter
{
    public async ValueTask<object> InvokeMethodAsync(
        HubInvocationContext context,
        Func<HubInvocationContext, ValueTask<object>> next)
    {
        // Validate all method parameters
        var validationResults = new List<ValidationResult>();
        var parameters = context.MethodArguments
            .Select(arg => new ValidationContext(arg) { MemberName = context.MethodName });

        foreach (var paramContext in parameters)
        {
            Validator.TryValidateObject(paramContext.ObjectInstance, paramContext, validationResults, validateAllProperties: true);
        }

        if (validationResults.Any())
        {
            // Wrap validation errors in a structured object
            var errors = validationResults.Select(r => new
            {
                Field = r.MemberNames.FirstOrDefault(),
                Message = r.ErrorMessage
            });

            // Throw a HubException with the structured error data
            throw new HubException("Validation failed", errors);
        }

        // Proceed to the hub method if validation passes
        return await next(context);
    }

    // Optional: Implement other filter lifecycle methods if needed
    public Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
    {
        return next(context);
    }

    public Task OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
    {
        return next(context);
    }
}

Step 2: Register the Filter in Startup/Program.cs

Add your filter to the SignalR services so it applies to all hubs:

// In Program.cs
builder.Services.AddSignalR()
    .AddHubFilter<ValidationHubFilter>();

2. Validate Directly in the Hub Method (Method-Level Control)

If you need more granular control (e.g., custom validation logic specific to a single method), you can handle validation directly inside your hub method. Here's how to return structured errors even when your method returns an IObservable<T>:

Example Hub Method with Validation

using Microsoft.AspNetCore.SignalR;
using System.ComponentModel.DataAnnotations;
using System.Reactive.Linq;
using System.Reactive.Disposables;
using System.Collections.Generic;

public class StreamHub : Hub
{
    public IObservable<MyStreamItem> StreamData(SomeRequestData request)
    {
        // Validate the request parameter
        var validationResults = new List<ValidationResult>();
        var validationContext = new ValidationContext(request);
        
        if (!Validator.TryValidateObject(request, validationContext, validationResults, validateAllProperties: true))
        {
            // Convert validation results to a structured error object
            var structuredErrors = validationResults.Select(r => new
            {
                Field = r.MemberNames.FirstOrDefault(),
                Message = r.ErrorMessage
            });

            // Return an observable that immediately emits an error
            return Observable.Throw<MyStreamItem>(new HubException("Validation failed", structuredErrors));
        }

        // If validation passes, proceed with your stream logic
        return Observable.Create<MyStreamItem>(observer =>
        {
            // Your stream generation logic here (e.g., periodic updates)
            observer.OnNext(new MyStreamItem { /* Populate data */ });
            observer.OnCompleted();

            return Disposable.Empty;
        });
    }
}

Key Notes for Method-Level Validation:

  • When returning an IObservable, use Observable.Throw to emit a validation error immediately—this will be caught by the client as an error.
  • Wrapping errors in HubException ensures the error is sent to the client in a structured way (instead of a generic connection error).

3. Handling Errors on the Client

No matter which approach you use, clients can catch the HubException and parse the structured error data. For example, in a JavaScript client:

try {
    const stream = connection.stream("StreamData", requestData);
    await for await (const item of stream) {
        // Handle stream items
    }
} catch (err) {
    if (err instanceof HubException) {
        // Access the structured errors
        const validationErrors = err.data;
        console.log("Validation failed:", validationErrors);
        // Display errors to the user (e.g., highlight invalid fields)
    } else {
        // Handle other connection/stream errors
    }
}

Which Approach Should You Choose?

  • HubFilter: Best for global validation rules that apply to all hub methods (e.g., data annotation validation). Reduces code duplication and keeps validation logic centralized.
  • Method-Level Validation: Ideal for custom, method-specific validation logic that can't be generalized. Gives you full control over error handling for individual methods.

Both approaches let you return structured validation errors instead of relying on default error types, so you can give clients clear, actionable feedback.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:24:10