SignalR 2.0 - 404 in IIS Virtual Directory - asp.net-mvc

I'm having an issue when deploying a very basic MVC5 app running SignalR 2.0.2. Everything works great in my local development environment when I'm running it with IIS Express. When I deploy to IIS, my js receives a 404 error attempting to connect to SignalR.
More specifically, I'm deploying to an application/virtual directory that is running under my Default Web Site. When I publish directly to Default Web Site, everything works successfully so IIS is not the issue.
GET http://myServer/signalr/negotiate?connectionData=%5B%5D&clientProtocol=1.3&_=1395517687175 404 (Not Found)
I'm assuming the 404 is caused by the missing application name. ie: myServer/MyApp/signalr/negotiate...
I've searched a number of posts and SignalR documentation with no luck regarding IIS and Applications/Virtual Directories and SignalR. Below is snippets of code in my app.
Thanks!
JS:
var connection = $.hubConnection();
var proxy = connection.createHubProxy('TestHub');
connection.start()
.done(function () {
console.log('Now connected, connection ID=' + connection.id + ' using transport=' + connection.transport.name);
})
.fail(function () { console.log('Could not connect'); });
Startup.cs:
app.MapSignalR();
Update
By changing the following JS code I was able to 'fix' the issue. The question is, how proper is this?
//var connection = $.hubConnection();
var connection = $.hubConnection("/MyApp/signalr", { useDefaultPath: false });

Your fix seems reasonable.
{ useDefaultPath: false } simply tells SignalR not to append "/signalr" to the url, so you could also create your connection object like this: var connection = $.hubConnection("/MyApp");
Alternatively, if you want to use JS hub proxies generated at /MyApp/signalr/hubs, you can could connect like this:
var proxy = $.connection.testHub;
// Make sure you always wire up client methods before calling start
proxy.client.myClientMethod = function () { /* ... */ };
$.connection.hub.start()
.done(function () { /* ... */ })
.fail(function () { /* ... */ });
http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#genproxy

A solution which will work in dev, and in IIS hosted as application, virtual directory or root is to configure the hub using the page url as its base. This will mean you won't need to hard code the value and negates configuration change for development and deployed scenarios.
var connection = $.hubConnection(document.location.origin + document.location.pathname);

Related

how to integrate signalr in typescript project?

I am using asp.net mvc 4.5 project with typescript in client side. I have installed and configured signalR in the server side. In the client side i have installed signalr.TypeScript.DefinitelyTyped with jquery,
i have referred signalr by below in my typescript file on the top,
///
and i have created interface file for my hub,
interface SignalR {
ForecastHub: {
client: {
DeleteProject(projectId: number)
};
server: {
send(name: string, message: string)
};
};
}
and my main code in ts is below,
var client = $.connection.ForecastHub;
client.client.DeleteProject = function (projectId) {
alert(projectId);
};
$.connection.hub.start().done(function () {
});
in the runtime i am getting an error 'Cannot read property 'ForecastHub' of undefined'
so how to import signalr in the typescript ts. please provide solution to integrate signalR in typescript project.
The below solution solves my problem,
How to load signalr.js in webpack inside Angular 2
Regards,
Venu Perumal

Ionic NTLM Authentication - IIS

I am building an iOS mobile application using the Ionic framework. The app will be accessing APIs that will be served by an ASP.NET 5 (MVC 6) application hosted on IIS using Integrated Windows Authentication. The server already has a web interface to it that uses an AngularJS client. I have been trying to get a $http call to the server from within an Ionic/Angularjs controller and have had no luck getting through the IIS Integrated windows authentication (I have tried running on the device/simulator as well as ionic serve). I always get a 401 Unauthorized error. I have tried setting withCredentials to true and passing in a username/password in the request with no luck. When I try to access the API URL from safari on an iPhone (a non-windows environment), I do get the Browser Authentication popup which successfully logs me in on entering my intranet windows username password.
I initially had some CORS issues that I have sorted through by adding the CORS service on the server side and also allowing all origins. I also have the proxy setup to avoid CORS issue when testing using ionic serve. Has anyone done something like this before? This is my controller code:
angular.module('starter.controllers', [])
.controller('AppCtrl', function($scope, $ionicModal, $http) {
$http.defaults.useXDomain = true;
$http.defaults.withCredentials = true;
// Form data for the login modal
$scope.loginData = {};
// Create the login modal that we will use later
$ionicModal.fromTemplateUrl('templates/login.html', {
scope: $scope
}).then(function(modal) {
$scope.modal = modal;
});
// Triggered in the login modal to close it
$scope.closeLogin = function() {
$scope.modal.hide();
};
// Open the login modal
$scope.login = function() {
$scope.modal.show();
};
// Perform the login action when the user submits the login form
$scope.doLogin = function() {
console.log('Doing login', $scope.loginData);
$http.post('http://localhost:8100/api/APIAccount/Login',{withCredentials:true})
.then(function(response)
{
console.log('success');
}, function(error) {
console.log('error');
});
};
});
After several hours of troubleshooting, it was as simple as setting up ASP.NET 5 CORS service to allow credentials. In my Startup.cs file in the ConfigureServices function I had to put in the following. Hope this helps someone else in the future.
services.AddCors(options =>
{
options.AddPolicy("AllowAllOrigins",
builder => builder.WithOrigins("http://<domainname>")
.AllowCredentials());
});

webapi odata update savechanges issue - Unable to connect to remote server

In my mvc webapplication, I am using webapi to connect to my database through odata.
Both MVC WebApp and Odata WebApi are on different ports of Azure cloud service webrole endpoints.
MVC WebApp - 80
Odata WebApi - 23900
When I do a odataproxy updateobject and call savechanges like
odataProxy.UpdateObject(xxx);
odataProxy.SaveChanges(System.Data.Services.Client.SaveChangesOptions.PatchOnUpdate);
I am getting a weird exception on savechanges method call - unable to connect to remote server.
When I tried to look into inner exceptions, It says that - No connection could be made as the target machine actively refused it 127.0.0.1:23901
So if you observe the port number in the exception, it shows as 23901 and obviously this error should come as the request is supposed to hit 23900.
I am facing this exception only when running on azure cloud solution. Whenever I do an update request, it fails by hitting a wrong port (added by 1).
Another thing is, apart from this updateobject -> savechanges, rest all works like fetching data and adding data.
FWIW, I've just run across this same thing. Darn near annoying and I really hope it doesn't happen in production. I'm surprised no other people have come across this though.
The idea of creating a new context, attaching the object(s) and calling SaveChanges really repulsed me because not only does it practically break all forms of testing, it causes debug code and production code to be fundamentally different.
I was however able to work around this problem in another way, by intercepting the request just before it goes out and using reflection to poke at some private fields in memory to "fix" the port number.
UPDATE: It's actually easier than this. We can intercept the request generation process with the BuildingRequest event. It goes something like this:
var context = new Context(baseUri);
context.BuildingRequest += (o, e) =>
{
FixPort(e);
};
Then the FixPort method just needs to test the port number and build a new Uri, attaching it back to the event args.
[Conditional("DEBUG")]
private static void FixPort(BuildingRequestEventArgs eventArgs)
{
int localPort = int.Parse(LOCAL_PORT);
if (eventArgs.RequestUri.Port != localPort)
{
var builder = new UriBuilder(eventArgs.RequestUri);
builder.Port = localPort;
eventArgs.RequestUri = builder.Uri;
}
}
Here's the original method using reflection and SendingRequest2, in case anyone is still interested.
First we create a context and attach a handler to the SendingRequest2 event:
var context = new Context(baseUri);
context.SendingRequest2 += (o, e) =>
{
FixPort(e.RequestMessage);
};
The FixPort method then handles rewriting the URL of the internal request, where LOCAL_PORT is the port you expect, in your case 23900:
[Conditional("DEBUG")]
private static void FixPort(IODataRequestMessage requestMessage)
{
var httpWebRequestMessage = requestMessage as HttpWebRequestMessage;
if (httpWebRequestMessage == null) return;
int localPort = int.Parse(LOCAL_PORT);
if (httpWebRequestMessage.HttpWebRequest.RequestUri.Port != localPort)
{
var builder = new UriBuilder(requestMessage.Url);
builder.Port = localPort;
var uriField = typeof (HttpWebRequest).GetField("_Uri",
BindingFlags.Instance | BindingFlags.NonPublic);
uriField.SetValue(httpWebRequestMessage.HttpWebRequest, builder.Uri);
}
}
I have found the root cause and a temporary workaround.
Cause:
When you hit WebApi through some port :23900 in Azure compute emulator and do an update or delete operation, somehow the last request is blocking the port and because of the port walking feature in Azure emulator, it is jumping to next port where there is no service available which is causing the issue.
Even this issue is found only in development emulators.
Temp Workaround:
Use a different proxy to attach to updated context object and then save from the other proxy object.
var odataProxy1 = xxx;
var obj = odataProxy1.xyz.FirstOrDefault();
obj.property1="abcd";
...//Other update assignments
var odataProxy2 = xxx;
odataProxy2.AttachTo("objEntitySet",obj);
odataProxy2.UpdateObject(obj)
odataProxy2.SaveChanges(ReplaceOrUpdate);

SignalR across domains: errors with not allowed by Access-Control-Allow-Origin

Trying to call signalR from another domain, and keep on getting this error:
XMLHttpRequest cannot load
localhost:62150/signalr/negotiate?_=1362242757692. Origin
localhost:4982 is not allowed by Access-Control-Allow-Origin.
This is the code I'm trying to run:
$(function () {
jQuery.support.cors = true;
$.connection.hub.url = 'http://localhost:62150/signalr';
$.connection.hub.start()
.done(function () { alert("Now connected!"); })
.fail(function () { alert("Could not Connect!"); });
});
jquery and jquery.signalr.js are loaded, localhost:62150/signalr/hubs responds with JS, localhost:62150/signalr/hubs/negotiate?_=1362243021215 returns JSON if I run this in browser - so its not a missing script or invalid path issue.
What I've tried:
http://coding4life.wordpress.com/2012/03/30/making-cross-domain-calls-in-signalr/
(setting jQuery.support.cors and $.connection.hub.url)
Adding custom header in web.config with "Access-Control-Allow-Origin" value="*"
(this works only in IIS ? )
Creating an http module that would return this header on every request. Also tried to return actual domain name instead of *.
And combinations of all of the above.
Anyone has any idea what else I can try ?
The serving app is a combination of MVC and WebAPI (don't think it makes any difference).
If I'm trying that code from same domain - it works.
If you are using 1.0 or higher have you enabled cross domain on the server? (it's disabled by default now)
RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true });
After wasting a couple of hours I think it's good to share my experience:
DO NOT add Access-Control-Allow-Origin to your web.config (yes it never sais to add it, but when trying things this is literally the first I did and left it there after a simple jquery cross-domain access test)
RouteTable.Routes.MapHubs( new HubConfiguration() { EnableCrossDomain = true } ); works just fine for classes inheriting Hub
RouteTable.Routes.MapConnection<MyConnection>( "foo", "/foo", new ConnectionConfiguration { EnableCrossDomain = true } ); this works just fine too for classes inheriting PersistentConnection

running express.js app in Asp.net MVC

I would like to run node.js with asp.net mvc for performing socket.io operations.
i've successfully included the node.js in asp.net mvc as described in here
my problem is how to run express.js in asp.net mvc,
i've performed the url rewritting in Global.asax.cs file like
void Application_BeginRequest(object sender, EventArgs e)
{
// Get current path
string CurrentPath = Request.Path.ToLower();
if (CurrentPath.StartsWith("/node"))
{ HttpContext MyContext = HttpContext.Current;
MyContext.RewritePath("/Node/index.js/");
}
}
so in the url http:localhost:1234/node should redirect me to the index.js file in Node folder
it is all working well but,
when i start coading express.js server in index.js like
var express = require('express');
var app = express.createServer();
app.get('/node/', function (req, res) {
res.send(' welcome to express ');
});
app.listen(process.env.PORT);
I got error saying Cannot GET /Node/index.js/
where am i missing? please guide me how to write express.js coading in asp.net mvc
Iam running this application in windows 7 32 bit system with IIS 8.0 express , and installed node versions are
iisnode.js - iisnode-full-iis7-v0.2.3-x86
node.js - node-v0.8.19-x86
Thank you.
Do you have a web.config entry for iisnode handling index.js?
iisnode is not set up as a .js handler by default, since there is far more client-side javascript than server-side javascript, so you need to explicitly turn on iisnode as a handler for that file.

Resources