We've recently implemented API authentication by implementing a custom AuthorizationFilterAttribute, using credentials stored in Azure Document DB. DocDB mandates everything use Async.
Through experimenting we found that WebApi2 synchronous controllers will use the OnAuthorizationAsync if present, and OnAuthorization if no async method. We also found that asyc controller methods can use either auth method. But I'm not 100% sure it is working correctly. We only saw that code did hit breakpoints.
Oddly, you can also override OnAuthorization mark it as async
public async override Task OnAuthorization(....)
This last method compiles and executes fine, but the controller will not wait for the auth filter to finish executing before the action method begins. Usually the result is an ASP error:
An asynchronous module or handler completed while an asynchronous operation was still pending
Seems like this manipulation of the override should have been a compile error and not allowed.
Regardless.... There are many mysteries about AuthorizationFilterAttribute and a few other posts exist about the confusion. Custom Authorization in Asp.net WebApi - what a mess?
My question is how do you know which will execute and in which order of precedence? It does appear if both exist in the filter, only one method is executed.
If your controller action is async, must you override the OnAuthorizationAsync method?
If you have async await in your auth logic, and are forced to use OnAuthorizationAsync (like I am), does this then mean I have to change all my controller actions to now all be async controller actions?
I can't find any documentation that lays out scenarios for async action filters.
If you take a look at the source code of AuthorizationFilterAttribute then you can see that the base implementation of OnAuthorizationAsync is the one actually calling OnAuthorization.
public virtual void OnAuthorization(HttpActionContext actionContext)
{
}
public virtual Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
try
{
OnAuthorization(actionContext);
}
catch (Exception ex)
{
return TaskHelpers.FromError(ex);
}
return TaskHelpers.Completed();
}
As you can see, you can actually override either method you want and you don't need to call the base implementation. Just choose the one which makes more since for your scenario - it doesn't matter if the controller is async or not.
And regarding your question about marking OnAuthorization itself as async - the code compiles since that's the way C# async support is designed, but it indeed causes the calling code to not wait for the async part to complete (it actually can't wait since the method is marked async void and not async Task. You can read more about async avoid here.
Related
Our application has a extra piece of a Middleware which is called after UseAuthentication and UseAuthorization. It's reponsible to load user data from cache or database. The data is then stored in HttpContext.Items.
We however recently felt that this is a little out of place. We currently believe, it makes more sense to handle this in a custom authorization handler. Since the authorization handler is responsible for setting the HttpContext.User. Setting the session, is extremely close in functionality to setting the User. Our application actually doesn't use HttpContext.User, but our custom Session object to keep track of certain data.
Overall the documentation doesn't clearly specific when to use custom Middleware or custom Authentication handlers.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-6.0
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0
Nothing relevant is mentioned in the performance considerations either.(https://learn.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-6.0).
Are there limitations or considerations that should be considered when deciding to implement custom Middleware or Authentication handles in .net-core?
public async Task Invoke(HttpContext context, ISessionManager sessionManager, ILogger<VarioClientSessionMiddleware> logger)
{
var token = ParseToken(context.Request.Headers.Authorization);
try
{
var session = sessionManager.GetSession(token);
if (session != null)
{
context.SetSessionState(session);
}
}
catch (InvalidSessionIdException ex)
{
logger.LogDebug("...");
}
await _next.Invoke(context);
}
I've been pouring through documentation and forums trying to understand SignalR but I'm pretty stuck.
What I'm trying to achieve is, in a chat application: store messages outside of the Hub so that each time a user joins the chat, they can see all messages that had been sent before they joined.
So it seemed like an external class was the way to do that so I got that working with dependency injection
In ChatHub.cs
namespace SignalRChat.Hubs
{
public class ChatHub: Hub
{
public IChatStorage _chatStorage;
public ChatHub(IChatStorage chatStorage)
{
_chatStorage = chatStorage;
}
// and so on
And I have a method in ChatHub to send a message to chatStorage, but I'm confused on how to send back a list of all messages from chatStorage to ChatHub, or even to JavaScript. It seemed like a Controller was the way to do that but I'm not sure how to call the controller's methods:
namespace SignalRChat.Controllers
{
public class ChatController: Controller
{
private IHubContext<ChatHub> _hubContext;
public ChatController(IHubContext<ChatHub> hubContext)
{
_hubContext = hubContext;
}
public void Send(List<Message> messages)
{
// to do: something where chatStorage calls this method, then this
// method uses _hubContext.Clients.All.SendAsync
// But, how do I even call Send()???
}
}
}
Fundamentally I just don't understand how to wire everything up. SignalR is just really confusing.
I get how the simple server Hub and client JavaScript relationship works. But, then with dependency injection I don't get why
public ChatHub(IChatStorage chatStorage)
{
_chatStorage = chatStorage;
}
works. I didn't change any code to say like new ChatHub(new IChatStorage). Microsoft's docs even say that SignalR only calls default Hub constructors.
In Startup.cs nothing seems to specify that I want to call ChatHub with a new chatStorage:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
services.AddSingleton<IChatStorage, ChatStorage>();
services.AddSingleton<ChatController>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// omitted some default code
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
So first question, how does that work? How does it know to pass an argument to the ChatHub constructor? I understand the services.AddSingleton part, just not how that gets "wired up".
Same thing with a controller class. How does the program know to pass an IHubContext object into its constructor? Where do you specify that?
Finally, how would you go about making this setup work? Current I'm trying to communicate from ChatHub->chatStorage->ChatController->ChatHub. And to do that I'm trying to pass a reference to chatController in chatStorage.
Not sure if it's clear what I'm asking. If anything I'm looking for a clear explanation on how these concepts all work together, rather than a specific solution to my code.
Thank you!
So first question, how does that work? How does it know to pass an argument to the ChatHub constructor? I understand the services.AddSingleton part, just not how that gets "wired up".
When SignalR instantiates your hub instance (the framework controls this, you don't), it will resolve any dependencies specified in your Hub's constructor. This is part of the dependency injection system that's part of .NET (as mentioned in the comments).
Same thing with a controller class. How does the program know to pass an IHubContext object into its constructor? Where do you specify that?
Same idea, but you didn't have to wire up IHubContext, that's something that AddSignalR does.
Finally, how would you go about making this setup work? Current I'm trying to communicate from ChatHub->chatStorage->ChatController->ChatHub. And to do that I'm trying to pass a reference to chatController in chatStorage.
It's not exactly clear to me what you want the interaction to be between the client and the server to both the hub and the controller.
What I'm trying to achieve is, in a chat application: store messages outside of the Hub so that each time a user joins the chat, they can see all messages that had been sent before they joined.
Going back to this original statement I can ask a few clarifying questions:
When the user writes a message in the chat room, are you calling the hub or a controller action (REST API)? That will determine where you need to inject the IChatStorage type. Once you receive a message, you'll stash the message in IChatStorage.
When the user joins the chat, they'll make a call to the server to retrieve all messages. This call will return messages stored in IChatStorage.
Assuming you want to use the hub for everything, you would expose methods on the hub do accomplish this. If you wanted to use REST API calls from the client, then you would use the controller.
I am using SignalR and .Net 5.0 and leveraging Hub Filters to execute code on incoming Invokations to my SignalR Hub.
I am looking for a way to do the same thing with outgoing messages from the Hub to the Client but seem to be coming up with no options.
Perhaps alternatively, I would love to hook into and execute code specifically when the built in Ping Messages are sent out.
It looks like similar functionality used to be possible in the old version's HubPipeLineModule but I have not been able to find any way to achieve this in current SignalR. Is this possible?
HubLifetimeManager is responsible for sending messages to clients and it is possible to inject your custom one, right before registering SignalR:
builder.Services.AddSingleton(typeof(HubLifetimeManager<>), typeof(CustomHubLifetimeManager<>));
builder.Services.AddSignalR();
where
public class CustomHubLifetimeManager<THub> : DefaultHubLifetimeManager<THub> where THub : Hub
{
public override Task SendAllAsync(string methodName, object?[] args, CancellationToken cancellationToken = default)
{
//todo: do your stuff
return base.SendAllAsync(methodName, args, cancellationToken);
}
//todo: override rest of the methods
}
It works fine, however this approach looks a bit hacky for me.
As we know that Rebus provides Topic Based routing in addition to the familiar TypeBased routing although we are told that the TypeBased routing follows the same principle.
On my side however unfortunately I have not seen a good example on how to create a handler that processes messages published to a particular topic.
Suppose I publish my message as follows
var message=new Student { StudentID=90008,FirstName="Chilipo",LastName="Mjengo" };
await bus.Advanced.Topics.Publish("rebus_example_topic", message);
In another endpoint I have subscribed to the topic as follows
await bus.Advanced.Topics.Subscribe("rebus_example_topic");
My interest is to know how do I then implement the Handler that will process the messages published to the rebus_example_topic.
Regards
It's quite simple, actually 🙂 the preferred way of writing message handlers is to implement IHandleMessage<TMessage>, where TMessage is your message type.
In your case that would be
public class StudentHandler : IHandleMessages<Student>
{
public async Task Handle(Student message)
{
// handle your message in here
}
}
How you then activate your message handler depends on which handler activator, you're using. The "handler activator" is what you use to instantiate message handlers, so you just need to register your handler in that to make it available to Rebus.
Some container integrations even come with additional help in the form of registration extensions, so e.g. if you're using Autofac, you can register your handler like this:
containerBuilder.RegisterHandler<StudentHandler>();
I have a Web Api 2.2 controller action like so:
[HttpPost]
[Route("{id}")]
public void Update(int id, [FromBody] MyData data) {}
HTTP clients make POST requests with an application/json message body, but sometimes the System.Net.Http.Formatting.JsonMediaTypeFormatter can't parse the JSON which the body contains, and throws an exception before the controller action is ever invoked.
In that case, I would like to log both the exception that the formatter threw and the JSON text which gave rise to the exception. I want this to happen in production (so as to properly account for the failure of failed requests), and therefore mere 'Tracing' is not an acceptable option here. I feel that this is quite an acceptable thing to want.
At first blush, the global error handling facility introduced with Web Api 2.1 would be just the thing here. But in my System.Web.Http.ExceptionHandling.IExceptionLogger, there is no longer any opportunity to read the message body:
public override void OnException(HttpActionExecutedContext context)
{ string body = await context.Request.Content.ReadAsStringAsync(); // essentially
// body is an empty string! the formatter already consumed it, and it's not available now
}
What options do I have for ASP.NET and/or Web Api 2 extension points to handle exceptions raised by the MediaTypeFormatter and log the actual request body?