Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
signalr / SignalR Programming in Microsoft ASP.NET.pdf
Скачиваний:
65
Добавлен:
25.05.2015
Размер:
19.23 Mб
Скачать

Sending messages to specific users

We previously saw that we can invoke a method on a specific client by using its connection identifier via a construct similar to Clients.Client(id).MethodToInvoke(params).

We also briefly commented on another option: using the User(userName) selector, thus adding an additional way of selecting the recipient or recipients of the message by their user names.

public Task ObiWanMessage()

{

return Clients.User("luke").Message("Use the force");

}

In the preceding example, the message will be sent to all the SignalR clients that are authenticated in the system as “luke”. Note that, as opposed to Clients.Client(), which locates the recipient uniquely, in this case it would be possible to send the message via several connections, as many as are associated to the authenticated user.

The small distinguishing nuance is the distinction between the terms “client” and “user.” A client is equivalent to a connection and has a unique identifier, whereas a user can have several active connections (for example, by having several tabs open in their browser), each one with a different identifier.

Nevertheless, the best thing about this feature is its flexibility. When SignalR needs to know the name of the user, it employs a class that implements the IUserIdProvider interface, whose single method will be in charge of returning the user name, using information from the current request, if necessary.

The default implementation is found in the PrincipalUserIdProvider class, defined in the Microsoft.AspNet.SignalR.Infrastructure namespace, and it returns the name of the authenticated user:

public class PrincipalUserIdProvider : IUserIdProvider

{

public string GetUserId(IRequest request)

{

if (request == null)

throw new ArgumentNullException("request");

if (request.User != null && request.User.Identity != null) return request.User.Identity.Name;

else

return (string) null;

}

}

68 Chapter 5Hubs

www.it-ebooks.info

Obviously, we can implement our own logic to obtain the name of the user associated to a connection. For example, the following code shows how we could use the content of a cookie for this:

public class CookiesUserIdProvider : IUserIdProvider

{

public string GetUserId(IRequest request)

{

if (request == null)

throw new ArgumentNullException("request"); Cookie cookie;

if (request.Cookies.TryGetValue("username", out cookie))

{

return cookie.Value;

}

else

{

return null;

}

}

}

SignalR will know that it must use this class instead of the one included out of the box because we are going to specifically indicate so by registering this class in a component called the dependency resolver. In Chapter 9, “Advanced topics,” we will delve into the dependency resolver. For now, it will suffice to understand that the following code, entered in the application startup, tells

SignalR that when it needs an object of the IUserIdProvider type it must use the delegate that we are providing it to obtain the instance. In this case, we will always provide it an instance of the

CookiesUserIdProvider type:

GlobalHost.DependencyResolver.Register(

typeof(IUserIdProvider),

()=> new CookiesUserIdProvider()

);

State maintenance

Clients automatically include state information in the calls they make to the methods of a hub. This information consists of a key-value dictionary that contains data that might be of use to either end. From the point of view of the server, this gives us the possibility of accessing these data from the body of the methods, again thanks to .NET dynamic types.

HubsChapter 5

69

www.it-ebooks.info

Thus, on the client side, we can create and use arbitrary properties that could be directly accessed from the server method invoked. In the case of a JavaScript client, these properties are created inside the state property of the proxy, as you can see in the following code:

Obviously, we can access data relative only to the client that invoked the current method; for this reason, we use the familiar Clients.Caller property to access such data.

It is also possible to modify the values at the server. The new state will be transferred to the client in the response to the invocation. Notice that in this case we are directly applying an autoincrement operator on the MsgId property:

public Task Alert(string msg)

{

var alert = string.Format("#{0} alert from {1}: {2}", Clients.Caller.MsgId++, Clients.Caller.UserName, msg);

return Clients.All.ShowAlert(alert);

}

The new value of the MsgId property will be returned to the client as part of the response to the call to the method. Upon arrival, the local value of the property will be updated.

This capability can be useful for the purpose of simplifying the signature of the methods of the hub, although we should bear in mind that the state information that we include at the client will travel in all requests, to maintain synchronization between both ends, so we must use it carefully.

Later, in “State maintenance” in the “Client implementation” section, we will look at the mechanisms behind this interesting feature.

70 Chapter 5Hubs

www.it-ebooks.info

Accessing information about the request context

When using hubs, in method signatures we include only the parameters that the methods need to receive from the client to perform the task assigned to them. Consequently, obtaining information from the context of the request or even the identifier of the connection that makes the call is not as direct as when we used persistent connections, where we received these data as parameters of the methods provided by the PersistentConnection base class.

Nevertheless, it is just as easy. To access the context data, the Hub class offers the Context property—of the HubCallerContext type—through which it exposes properties, including

ConnectionId, Headers, QueryString, or User. See Figure 5-4.

FIGURE 5-4  Members of the Context property of the Hub class.

As its name implies, in Context.ConnectionId we will always have the identifier of the connection that the hub is currently instantiating and executing.

public Task NewUser()

{

var message = "New user: " + Context.ConnectionId; return Clients.Others.ShowAlert(message);

}

Other properties of Context, such as Headers, QueryString, RequestCookies, and User, are just shortcuts to members of the Request property, which is the one that really contains the information about the request.

Although one might expect the type of this Request property to be the traditional System

.Web.HttpRequest, this is not so. The property uses the IRequest type, which is a SignalR-specific abstraction and which allows accessing information on the context of the request in a decoupled way, without needing to know exactly what its implementation is. If we keep poring over the code of the framework, we will see that this interface is implemented in a class that, in turn, obtains all the data

it needs using the OWIN standard. This constitutes a complete isolation from the host in which the application is executed.

HubsChapter 5

71

www.it-ebooks.info

Соседние файлы в папке signalr