How do I update multiple users on Blazor Server-Side? - signalr

Suppose I want a UI where users in a group have a synchronized view. Imagine a group-chat application, where everyone in the group should see the exact same messages.
When a user posts a message to the group, the entire group needs to receive it.
With JS, I might use SignalR Groups, and have the front-end generate a SignalR event with a message is posted. The server-side hub would then send that message out to the group.
On Server-Side Blazor, since all the users' states are already on the server, how would I coordinate updating the UI of groups of users on Blazor?

I was trying to achieve something similar. After a while, I discovered a way of doing this. Kyle's answer helped me figure it out.
You must create a class, let's call Messenger, which has an Action property, and a method that invokes this Action once called
public Action OnMessage { get; set; }
public void AddMessage(string message)
{
Text = message;
OnMessage?.Invoke();
}
In the razor component, inject the Messenger. Also, you should set an event to the OnMessage property, and then call StateHasChanged in order to tell the view that it can update
#inject Messenger messenger
// rest of component here, and then the code block
protected override async Task OnInitializedAsync()
{
messenger.OnMessage += () =>
{
// do something with the messenger.Text here
InvokeAsync(this.StateHasChanged);
};
}
Don't forget, on your Startup class, add the class Messenger as a Singleton in order to have it available for injection
services.AddSingleton<Messenger>();
I hope that is what you were trying to achieve, and it can help others as well.
Cheers.

Related

Is a centralized processing of request header data possible?

Let me explain what my problem is:
In a Web API project I am facing the issue that every single request which is sent to my controller has to contain some header data which should be processed before the controller action runs.
So as far as I know, I could include a new argument on every single action and decorate it with a FromHeader Attribute. Currently there are about 2000 actions from several controllers where I would have to change the parameter signature. So it would be a much prettier solution, if I could centralize the processing of this header data.
I am looking for some code to overwrite which is between the constructor of the controller class and the execution of the controller's action. Does anybody know a method for overriding or a pattern to accomplish this requirement? May be there is a possibility to extend the routing to the action
public override void OnActionExecuting(ActionExecutingContext context)
{
/*
PUT YOUR CODE HERE ;-)
*/
base.OnActionExecuting(context);
}

How can I access the request HttpSession from Scout client side code?

For example, from Scout Form execStore() method, right before executing any server services, i like to get the HttpSession and eventually get custom data from its attributes store.
As mentioned in the comments, Eclipse Scout separates the UI Layer (the HTML rendering - or the Swing client in older versions) from the client model. And while the UI Layer knows about the HttpSession, the client model, in which your form lives, does not.
You can however put the relevant attributes on the ServerSession (backend) and synchronize them to the ClientSession (model) or vice versa - depending on where your attributes come from.
This sketch should get you started:
In your Client/ServerSession class (extends AbstractServerSession) add a getter and setter.
If - and only if - you need to synchronize the values to the client implement the getter / setter like this (example for an Integer property):
public Integer getMyProperty() {
return getSharedContextVariable("myProperty", Integer.class);
}
public void setMyProperty(Integer newValue) {
setSharedContextVariable("myProperty", Integer.class, newValue);
}
You'll need to teach the application to transfer the data to your Client or ServerSession.
If your data comes from the backend side (e.g. from database): Your best guess is to override the default implementation of org.eclipse.scout.rt.server.context.HttpServerRunContextProducer.
Create a subclass of this class in your .server-part, and add the #Replace annotation. Your best place to implement it is likely in the method public IServerSession getOrCreateScoutSession(HttpServletRequest req, ServerRunContext serverRunContextForSessionStart, String scoutSessionId)
If your data comes from the UI side (e.g. passed by SAML):
This is more complicated and I have only hints where to start looking: org.eclipse.scout.rt.ui.html.UiSession.createAndStartClientSession(Locale, UserAgent, Map<String, String>) on how the ClientSession is created and if you can access your data at this location.

web api private chat message filter asp.net

I would like to create message filter between 2 users. What i mean by filter is that those 2 users will only see their messages and not others just to make it clear.
I have GET method for getting messages like this in my controller where i specify in params from who and to who are those messages. Problem is i dont know how i can pass those 2 params into my method.
I have idea about calling this method from my ChatHub (SignalR) but this clearly isnt best idea so there must be another way how to make it work.
public async Task<IEnumerable<PrivateChatMessageDTO>> GetPrivateMessages(string fromUserId, string toUserId)
{
return await _service.GetPrivateMessages(fromUserId, toUserId);
}

How to manage an instance with a specific lifetime

I have two screens which need to manage a list of items. The user can navigate from 1st screen to 2nd screen.
When user navigates back from 1st screen, the list of items needs to be saved to a store.
The way I was thinking to do it having a service holding the collection which gets injected in the view-models.
But the service instance must be released once user navigates back from 1st screen:
// somewhere, maybe in Application:
LifetimeManager = new ContainerControlledLifetimeManager();
Container.RegisterType<Service>(lifetimeManager);
class FirstPage()
{
ContainerControlledLifetimeManager _lifetimeManager;
public FirstPage()
{
InitializeComponent();
}
void OnDisappearing()
{
LifetimeManager.RemoveValue();
}
}
But this seems awkward, there has to be a better way to do this...
I see there's a ExternallyControlledLifetimeManager which seems it's what I need, but it would still require for FirstPage instance to get the instance of the manager and call RemoveValue. Or, I would call GC.Collect on OnDisappearing to make sure the GC collects the instance of the service, and ExternallyControlledLifetimeManager will recreate it next time it needs to be injected in the FirstPageViewModel. But calling GC.Collect seems wrong too...
Based on your comments, it sounds like you should be passing your data to your views via navigation parameters. Then each page can handle the parameters differently. Keep it simple.
var p = new NavigationParameters();
p.Add("items", MyListOfItems);
_regionManager.RequestNavigate("MyView", p);
Put your Service in another service whose job is to provide the current instance of Service.
interface IServiceProvider
{
Service CurrentInstance { get; set; }
}
Then register this one as a singleton (ContainerControlledLifetimeManager) and use it in both view models. The first one sets the instance to a new one, most likely created by some IServiceFactory, and the second one fills the instance with data.

ASP.NET Web API - method that is called for all requests prior to the routed method?

I'm writing in C# for ASP.NET Web API 2. What I want is a catch-all method that will execute for every single request that comes to my Web API.
If the method returns null, then the original routing should continue, seeking out the correct method. However, if the method returns, say, an HTTPResponseMessage, the server should return that response and not proceed on to normal routing.
The use case would be the ability to handle various scenarios that may impact the entire API. For example: ban a single IP address, block (or whitelist) certain user agents, deal with API call counting (e.g. someone can only make X requests to any API method in Y minutes).
The only way I can imagine to do this right now is to literally include a method call in each and every new method I write for my API. For example,
[HttpGet]
public HttpResponseMessage myNewMethod()
{
// I want to avoid having to do this in every single method.
var check = methodThatEitherReturnsResponseOrNull(Request);
if (check != null) return (HttpResponseMessage)check;
// The method returned null so we go ahead with normal processing.
...
}
Is there some way to accomplish this in routing?
This is what Action Filters are for. These are Attributes that you can place either globally, at the class (Controller), or at the method (Action) levels. These attributes can do preprocessing where you execute some code before your action executes or post processing where you execute code after the action executes.
When using pre processing you have the option to return a result to the caller and not have your method (action) be fired at all. This is good for model validation, authorization checks, etc.
To register a filter globally edit the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourFilterAttribute()); // add record
// rest of code
}
}
To create a custom attribute inherit from System.Web.Http.Filters.ActionFilterAttribute or you can implement interface System.Web.Http.Filters.IActionFilter or you can implement IAuthorizationFilter/AuthorizationFilterAttribute if you specifically want to allow/deny a request.
It also sounds like you want to create multiple attributes, one for each role like IP filtering or count calling etc. That way it would be more modular instead of one enormous authorization filter.
There are many tutorials out there like this one (chosen at random in my Google search results). I am not going to post code because you did not do so either so I would just be guessing as to what you wanted to do.

Resources