I'm working with SignalR project, in which I want to use Hub in WebApi project as well as Web project. So I've created one class library project and implemented Hub over there.
My project structure looks like:
-ChatHub
-Hub
-Webapi
-Website
Here is my Hub:
[HubName("chathub")]
public class ChatHub : Hub
{
public override Task OnConnected()
{
return base.OnConnected();
}
public override Task OnReconnected()
{
return base.OnReconnected();
}
}
When I calling Hub from my website it's working well.
<script src="~/signalr/hubs"></script>
var chatHub = $.connection.chathub;
Here is how I connect Hub from outside(Android):
mHubConnection = new HubConnection(http://{IpAddress}/ChatApp/);
mHubProxy = mHubConnection.createHubProxy(chathub);
API:
public IHttpActionResult LoginUser([FromBody]LoginModel model)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
//chatUser logic here
hubContext.Clients.Client(chatUser.ConnectionId).receiver(response);
}
But it gives me an error:
java.util.concurrent.ExecutionException:
microsoft.aspnet.signalr.client.transport.NegotiationException: There
was a problem in the negotiation with the server
10-13 18:15:54.074 18686-18686/com.chatapp.android W/System.err:
Caused by:
microsoft.aspnet.signalr.client.http.InvalidHttpStatusCodeException:
Invalid status code: 404
How can we connect Hub if my Hub is out side of API project?
I've gone through Sharing a SignalR hub between a WebApi and MVC project but didn't get the answer they were provided.
Are you calling mHubConnection.Start() after setting up the connection and the proxy to the hub? Also is the url being passed into the HubConnection constructor the correct location for the hub? Here are a couple of links that might be helpful, if you haven't already been through them: Access hub from .NET client, configure signalr url
Related
I have a Web API endpoint that creates a record and emits a successful 201 Created when the record is created.
Is it possible send a notification to a standalone HTML/JS web page that the record is created as it gets created using SignalR?
How can I create this publisher in the Web API and how to subscribe to it from the standalone webpage?
Yes - it is possible so long as that browser has an active connection to the SignalR Hub.
You can use this code as a starting point. It assumes you have a SignalR Hub class named MessageHub. This broadcasts a message to all active SignalR clients.
[RoutePrefix("api/messaging")]
public class MessagingController : ApiController
{
[Route("")]
public void Post(Message message)
{
var notificationHub = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
if (notificationHub != null)
{
try
{
// create your record
notificationHub.Clients.All.creationSuccess(message);
}
catch (Exception ex)
{
// do a thing
}
}
}
}
creationSuccess is the name of the function on the client side (JavaScript) which will handle the notification inside of the browser to which you're referring. Here's more information regarding the details of that
So I am very new to SignalR, in fact I've only been using it for a couple of days now. Anyway, I am getting the error below when my application first starts up:
The code for the application in question is located in two projects, a Web API and a Single Page Application (SPA). The first one has my backend code (C#) and the second one my client-side code (AngularJS). I think the problem might be due to the fact that the projects in question run on different ports. The Web API, where my SignalR hub lives, is on port 60161 and the SPA is on 60813. My hub is declared like so:
public class ReportHub : Hub
{
public void SendReportProgress(IList<ReportProgress> reportProgress)
{
this.Clients.All.broadcastReportProgress(reportProgress);
}
public override Task OnConnected()
{
this.Clients.All.newConnection();
return base.OnConnected();
}
}
and then in my Startup.cs file for my Web API I initialize SignalR like this:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.Services.Replace(typeof(IHttpControllerActivator), new NinjectFactory());
config.MessageHandlers.Add(new MessageHandler());
//set up OAuth and Cors
this.ConfigureOAuth(app);
config.EnableCors();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Setting up SignalR
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration { EnableJSONP = true });
});
//set up json formatters
FormatterConfig.RegisterFormatters(config.Formatters);
WebApiConfig.Register(config);
app.UseWebApi(config);
}
For my client-side code I use an Angular SignalR API called angular-signalr-hub (Angular-signalr-hub). The client-side follows:
angular
.module("mainApp")
.factory("reportHubService", ["$rootScope", "Hub", reportHubService]);
/// The factory function
function reportHubService($rootScope, Hub) {
var vm = this;
vm.reportName = "None";
// Setting up the SignalR hub
var hub = new Hub("reportHub", {
listeners: {
'newConnection': function(id) {
vm.reportName = "SignalR connected!";
$rootScope.$apply();
},
'broadcastReportProgress': function (reportProgress) {
vm.reportName = reportProgress.reportName;
$rootScope.$apply();
}
},
errorHandler: function(error) {
},
hubDisconnected: function () {
if (hub.connection.lastError) {
hub.connection.start();
}
},
transport: 'webSockets',
logging: true
//rootPath: 'http://localhost:60161/signalr'
});
I did some googling yesterday and one of the suggestions I came upon was to set the SignalR URL to the one of my Web API, which I did (the commented out line above). When I uncomment the line in question, that does seem to do something because if I now go to http://localhost:60161/signalr/hubs in my browser, it does show me the dynamically generated proxy file:
and when I run my application I no longer get the error above, but now it doesn't seem to connect. It gets to the negotiate line and it stops there:
I think it should look like this (this is from a SignalR tutorial I found):
In addition, none of my listeners (declared in my Angular code above) get called, so something is still now working quite right. There should be more lines in the log to the effect that connection was successfully established, etc. What could be the problem here?
UPDATE: upon further debugging i found out the problem is most likely being caused by the ProtocolVersion property being different between the client and the result here:
Because of that it seems it just exists and fails to establish connection.
I figured out what the problem was. My SignalR dependencies were out of date and because of that my client and server versions differed. All I had to do was update (via NuGet Package Manager) all SignalR dependencies to the latest version and now it works.
As a side note, SignalR was not very good at telling me what was wrong. In fact, no error message was displayed, unless of course there was some additional logging somewhere that had to be found or turned on, in addition to the logging I already had (turned on). Either way, it's either not logging certain errors or it makes it difficult to figure out how to turn on all logging. I had to go and debug the JQuery SignalR api to figure out what the problem was, which was a time consuming endeavour.
I have created a backend service using Azure Mobile App. Offline sync works fine with mobile clients using provided SDK. All the controllers that I have used are TableController. Now I want to add a simple Web API 2 controller ApiController, that will not be used by mobile clients.
Here is a simple ApiController that I have added to Controllers folder:
public class SimpleController : ApiController
{
public string Get()
{
return "Hello";
}
}
But the controller is never hit. If I add [MobileAppController] attrebute to the controller, it works but now it asks for additional headers in the request (I guess this headers are sent by client SDK):
{"message":"No API version was specified in the request, this request needs to specify a ZUMO-API-VERSION of '2.0.0'. For more information and supported clients see: http://go.microsoft.com/fwlink/?LinkId=690568#2.0.0"}
But I do not need this additional functionality here - I just want my service to respond to simple GET requests.
Although the guide states that it is not necessary to decorate the class:
Any controller that does not have MobileAppControllerAttribute applied can still be accessed by clients, but it may not be correctly consumed by clients using any Mobile App client SDK.
I can not achieve this. Am I missing something?
Thanks
I have figured out how to use both types of controllers.
Just add a call to config.MapHttpAttributeRoutes(); in your StartUp.ConfigureMobileApp method like this:
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//For more information on Web API tracing, see http://go.microsoft.com/fwlink/?LinkId=620686
config.EnableSystemDiagnosticsTracing();
config.MapHttpAttributeRoutes(); //Add this line
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
... //rest of the code
}
And then decorate your controller with custom route:
[Route("api/Simple")] //add this
public class SimpleController : ApiController
{
public string Get()
{
return "Hello";
}
}
I have a SignalR Hub with a non-static method that adds creates a new group based on the email address entered in a form:
public class EmailHub : Hub
{
public void AddEmail(string email)
{
base.Groups.Add(base.Context.ConnectionId, email);
}
}
I would like to call this Hub method from my MVC controller. My method currently looks something like this:
public class MyController : Controller
{
public ActionResult AddEmail(string email)
{
var hub = GlobalHost.ConnectionManager.GetHubContext<EmailHub>();
hub.Clients.All.AddEmail(email);
return View();
}
}
However, the code in the controller does not call the hub method. What can I change to be able to invoke the hub method successfully?
You'd have to pass your ConnectionId as a parameter, and you can't get that until SignalR is already connected.
SignalR connections are only present for one "page view" on the client. In other words, if I go to /chat/rooms/1, I get a ConnectionId, then if I navigate to /chat/rooms/2, I get a different ConnectionId. Because of that, base.Context.ConnectionId essentially doesn't exist when you're trying to use it here.
That leaves you with two options.
Subscribe to updates after SignalR connects on each page. In this scenario, you'd file a typical AddEmail request, then in JavaScript after that View() loads, you load SignalR, connect, then file a hub.server.addEmail(email). This is a standard approach in SignalR.
This is essentially the same thing, but if you were using an SPA framework that lets you persist your SignalR connection between views, that would work. Of course, that's a pretty significant change.
I've based all of this on the assumption that your action AddEmail is actually a page, which I inferred from that it returns a ViewResult. If that's called with AJAX, you could just append the ConnectionId as a query parameter and all of this would be moot.
I installed SignalR 2.0-rc1, and:
1: Created a hub:
public class Socials : Hub
{
public void PublicChat(string message)
{
Clients.All.PublicChat(new { message });
}
}
2: Created a startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
3: Registered it in web.config:
<add key="owin:AppStartup" value="Scyk.Startup, Scyk"/> //Scyk is my main namespace, also a project name, I placed Startup class in there.
Now, https://myhost.com/signalr/hubs is generating javascript file properly, but when I open developer console in my browser, I see that it has not connected, but:
There is an asp error saying that path /signalr/connect was not found (why is it trying to access /signalr/connect? Is that normal? If so, then this must be purely routing problem, how do I solve it?)
In my console, I see that there is a EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection. error. I am not sure if this is related, but it started to show up today, wasn't there before.
What am I doing wrong?
Any path beginning with /signalr should be routed through OWIN so signalr can handle the request.
It is normal for the client to try to access /signalr/connect after accessing /signalr/negotiate. /signalr/connect is the endpoint where SignalR establishes its WebSockets/Server-Sent Events/Forever Frame/Long Polling connections.