-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Hub filters! #21278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hub filters! #21278
Conversation
Spoke to @BrennanConroy about the design. Instead of building the pipeline dynamically per invocation we'll have a stable pipeline built once and creation of the specific HubFilters per invocation: e.g. var filters = new List<IHubFilter>()
{
new AHubFilter(),
new HubFilterFactory(typeof(BHubFilter)),
new CHubFilter(),
new FilterFactory(typeof(DHubFilter)),
}; Instead of storing a list of objects we can store a list of IHubFilter and have the HubFilterFactory create the HubFilter per call. |
(methodInvoked, result) = await ExecuteHubMethod(methodExecutor, hub, arguments, connection, scope.ServiceProvider); | ||
if (!methodInvoked) | ||
{ | ||
await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems overly defensive to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BrennanConroy After re-thinking this can we just support a way to return an invocation error instead of inferring anything? We can treat it like HubException
? We support a way that you can opt into sending a custom InvocationError but we don't do anything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we just support a way to return an invocation error
That's what the HubResult
is for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undo
5bcbada
to
278a092
Compare
I think it's time to undraft this PR and finish up. It's looking pretty good. |
One more thing we need to think about is non-conforming containers or @dotnetjunkie won't let me hear the end of it 😉 |
Thanks for pinning me, @davidfowl. As far as I can see, non-conforming containers would be able to plug in to this system, because:
Using Pure DI in this model will perhaps be more complicated, but certainly possible. Here's a sample implementation for Simple Injector: // Setup
services.AddSignalR(options =>
{
options.AddFilter(new SimpleInjectorHubFilterProxy<CustomHubFilter>(container));
})
class SimpleInjectorHubFilterProxy<THubFilter> : IHubFilter
where THubFilter : IHubFilter
{
private readonly InstanceProducer<THubFilter> producer;
public SimpleInjectorHubFilterProxy(Container container) =>
producer = Lifestyle.Scoped.CreateProducer<IHubFilter, THubFilter>(container);
private IHubFilter GetFilter() => this.producer.GetInstance();
public async ValueTask<object> InvokeMethodAsync(
HubInvocationContext context, Func<HubInvocationContext, ValueTask<object>> next) =>
this.GetFilter().InvokeMethodAsync(context, next);
public async Task OnConnectedAsync(
HubLifetimeContext context, Func<HubLifetimeContext, Task> next) =>
this.GetFilter().OnConnectedAsync(context, next);
public async Task OnDisconnectedAsync(
HubLifetimeContext c, Exception ex, Func<HubLifetimeContext, Exception, Task> next) =>
this.GetFilter().OnDisconnectedAsync(c, ex, next);
} Do note that this implementation doesn't use the provided |
@dotnetjunkie Thanks for reviewing, yes that pattern works well! |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
public string HubMethodName { get; } | ||
|
||
/// <summary> | ||
/// Gets the arguments provided by the client. | ||
/// </summary> | ||
public IReadOnlyList<object> HubMethodArguments { get; } | ||
public IReadOnlyList<object> HubMethodArguments { get => Arguments; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public IReadOnlyList<object> HubMethodArguments { get => Arguments; } | |
public IReadOnlyList<object> HubMethodArgument => Arguments; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just Arguments
for the public property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not make another breaking change
/// </summary> | ||
public Type HubType { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we retain this property to avoid a breaking change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not breaking because this was added in 5.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks swanky!
public string HubMethodName { get; } | ||
|
||
/// <summary> | ||
/// Gets the arguments provided by the client. | ||
/// </summary> | ||
public IReadOnlyList<object> HubMethodArguments { get; } | ||
public IReadOnlyList<object> HubMethodArguments { get => Arguments; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just Arguments
for the public property?
hubActivator = scope.ServiceProvider.GetRequiredService<IHubActivator<THub>>(); | ||
hub = hubActivator.Create(); | ||
|
||
if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection, descriptor, hubMethodInvocationMessage.Arguments, hub)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make sure to call out the ordering of Filters and Authorization in docs. I do remember @rynowak cautioning us against the complexity of moving things like AuthZ into filters, so I won't suggest that, but we should at least doc the order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yah we did that in the old signalr as well but I don’t see the need here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but we need to doc the fixed "ordering" we have so people know when filters run.
namespace Microsoft.AspNetCore.SignalR | ||
{ | ||
/// <summary> | ||
/// Methods to add <see cref="IHubFilter"/>'s to Hubs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Methods to add <see cref="IHubFilter"/>'s to Hubs. | |
/// Methods to add <see cref="IHubFilter"/> instances to Hubs. |
src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp.cs
Outdated
Show resolved
Hide resolved
Great work @BrennanConroy! |
public System.Collections.Generic.IReadOnlyList<object> HubMethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } | ||
[System.ObsoleteAttribute("This property is obsolete and will be removed in a future version. Use HubMethod.Name instead.")] | ||
public string HubMethodName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we decided in API review that we were not going to obsolete HubMethodName.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, maybe. No one commented that so it was forgotten. Fortunately we can easily fix this :)
Fixes #5353
Late feedback welcome!