SignalR not working with DelegatingHandler - asp.net-web-api

We have a delegating-handler that catches requests with a certain url prefix, and then reroutes them behind the firewall with the fed auth cookie attached...
This is working for our WebApi layer, but SingalR is firing off requests on its own while it is trying to connect that doesn't follow the pattern... I can't figure out how to force it to use the proper url prefix.
This is the url that is generated from the post request when it is trying to do long-polling: https://localhost:44330/signalr/connect?transport=longPolling&
See that it hasn't put the '/qsixlsignalr' into the url, which my delegating handler will be looking for.
var signalRBaseURL = "/qsixlsignalr"
$(function () {
// http://stackoverflow.com/questions/15467373/signalr-1-0-1-cross-domain-request-cors-with-chrome
$.support.cors = false;
var connection = $.hubConnection(signalRBaseURL);
var myHub = connection.createHubProxy('xlHub');
myHub.on('notify', function (message) {
alertsViewModel.refreshActiveCount(localStorage.getItem(PROJECT_ID));
if (window.location.pathname == '/' || window.location.pathname == '') {
alertsViewModel.refresh(localStorage.getItem(PROJECT_ID));
}
toastr.success(message);
});
connection.disconnected(function () {
setTimeout(function () {
connection.start();
}, 3000);
});
connection.logging = true;
connection.start();
});

If I remember correctly you need to tell SignalR explicitly that you don't want to use the the default url
var connection = $.hubConnection(signalRBaseURL, { useDefaultPath: false });

Related

Ajax request with CORS redirect fails in IE11

I'm trying to make an ajax request to a resource on the same domain. Under certain circumstances the request gets redirected(303) to an external resource. The external resource supports CORS.
In browsers like Chrome, Firefox or Safari the request succeeds.
In IE11 the request fails with error:
SCRIPT 7002: XMLHttpRequest: Network Error 0x4c7, The operation was canceled by the user
The ajax request is made with jQuery:
$.ajax({
url: "/data",
type: "POST",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify({name: 'John Doe'})
}).done(function () {
console.log('succeeded');
}).fail(function () {
console.log('failed');
});
I've build a little example which demonstrates the problem. You could see the code here.
w/o redirect
w/ redirect
Is there a way to solve this problem? What am I missing?
In the initial definition of the CORS-standard, redirects after a successful CORS-preflight request were not allowed.
IE11 implements this (now outdated) standard.
Since August 2016, this has changed, and all major browsers now support it (Here's the actual pull request).
I'm afraid to support <=IE11 you'll have to modify your server-side code as well to not issue a redirect (at least for <=IE11).
Part 1) Server-side (I'm using node.js express here):
function _isIE (request) {
let userAgent = request.headers['user-agent']
return userAgent.indexOf("MSIE ") > 0 || userAgent.indexOf("Trident/") > 0
}
router.post('data', function (request, response) {
if (_isIE(request)) {
// perform action
res.set('Content-Type', 'text/plain')
return res.status(200).send(`${redirectionTarget}`)
} else {
// perform action
response.redirect(redirectionTarget)
}
})
Part 2 Client-side
Note: This is pure Javascript, but you can easily adapt it to your jQuery/ajax implementation.
var isInternetExplorer = (function () {
var ua = window.navigator.userAgent
return ua.indexOf("MSIE ") > 0 || ua.indexOf("Trident/") > 0
})()
function requestResource (link, successFn, forcedRedirect) {
var http
if (window.XMLHttpRequest) {
http = new XMLHttpRequest()
} else if (window.XDomainRequest) {
http = new XDomainRequest()
} else {
http = new ActiveXObject("Microsoft.XMLHTTP")
}
http.onreadystatechange = function () {
var OK = 200
if (http.readyState === XMLHttpRequest.DONE) {
if (http.status === OK && successFn) {
if (isInternetExplorer && !forcedRedirect) {
return requestResource(http.responseText, successFn, true)
} else {
successFn(http.responseText)
}
}
}
}
http.onerror = http.ontimeout = function () {
console.error('An error occured requesting '+link+' (code: '+http.status+'): '+http.responseText)
}
http.open('GET', link)
http.send(null)
}
its already answered - have a look - https://blogs.msdn.microsoft.com/webdev/2013/10/28/sending-a-cors-request-in-ie/

how to make a middleware based on koa, which is used for Intercept HTTP response?

My project base on koa,I want to intercept HTTP response,when the response's message is "no promission",then excute 'this.redirect()'.
Your middleware (interceptor in my example) can access the response body after it yield next, so just place your logic after it yields.
var route = require('koa-route');
var app = require('koa')();
var interceptor = function*(next) {
// wait for downstream middleware/handlers to execute
// so that we can inspect the response
yield next;
// our handler has run and set the response body,
// so now we can access it
console.log('Response body:', this.body);
if (this.body === 'no promission') {
this.redirect('/somewhere');
}
};
app.use(interceptor);
app.use(route.get('/', function*() {
this.body = 'no promission';
}));
app.listen(3001, function() {
console.log('Listening on 3001...');
});

socketio client: How to handle socketio server down

I've got a socketio server/client working well together, however I want to start writing events for when the server is offline on page load or during normal run.
I'm including the remote socket.io code in my header:
<script src="<?=NODE_HOST?>/socket.io/socket.io.js"></script>
<script>
var nodeHost = '<?=NODE_HOST?>';
</script>
And in my client controller I have
if(typeof io != 'undefined')
this.socket = io.connect(this.settings.server);
else
this.handleDisconnect();
The function I have to attempt to re-connect over and over if a) A socket disconnect occurs during normal operation, or b) the server is down on page load
botController.prototype.handleDisconnect = function() {
$.getScript(nodeHost+"/socket.io/socket.io.js").done(function(script, textStatus) {
bot.control.socket = io.connect(bot.control.settings.server);
}).fail(function(jqxhr, settings, exception) {
setTimeout(function() {
bot.control.handleDisconnect();
}, 5000);
});
}
Am I going about this the correct way?
The main issue I have right now (which made me create this question) is my code errors on page load when the server is down because I have functions like:
socket.on(...
When socket doesn't yet exist. I could wrap those in a function and call it when I detect the global socket object exists on successful reconnection? Would it matter if that function that contains socket.on... is called multiple times (if the server goes down more than once during operation)?
OK I managed to come up with this solution that seems to work well using yepnope which I already had using Modernizr (it handles the cross domain issue for me too).
<script src="<?=NODE_HOST?>/socket.io/socket.io.js"></script>
<script>
var nodeHost = '<?=NODE_HOST?>';
</script>
// Attempt to connect to nodejs server
botController.prototype.start = function() {
// Is our nodejs server up yet?
if(typeof io != 'undefined') {
this.socket = io.connect(this.settings.server);
this.startSocketEvents();
} else {
this.handleDisconnect();
}
}
// Our connection to the server has been lost, we need to keep
// trying to get it back until we have it!
botController.prototype.handleDisconnect = function(destroySocketObject) {
if(destroySocketObject === undefined)
destroySocketObject = true;
// Destroy any cached io object before requesting the script again
if(destroySocketObject)
io = undefined;
yepnope.injectJs(nodeHost+"/socket.io/socket.io.js",
function(result) {
// Did it actually download the script OK?
if(typeof io != 'undefined') {
bot.control.socket = io.connect(bot.control.settings.server);
bot.control.startSocketEvents();
} else {
setTimeout(function() {
bot.control.handleDisconnect(false);
}, 5000);
}
}
);
Where startSocketEvents() function contains all of my socket.on events

Clicking the Back button changes the URL but not the page, for pages with AJAX loaded sections and using Backbone.history.navigate

I'm using a Backbone Router with Backbone.history.start({pushState: true, root: "/"}); and I'm loading sections of pages through AJAX and use Backbone.history.navigate(url, true); to make sure the url shown by the browser points to the corresponding section.
For instance, I load a page with the relative url "/username/profile", then load a section on that page ('Favorite Books') by clicking on a tab and triggering an AJAX request, and then change the url to "/username/favorite_book".
The problem is that if I go back (with the Back button) to a previous section from the one loaded through ajax, the page content does not change even though the url changes.
I have seen previous posts talking about Ajax Browser History, but I would like to know what should I do in the context of Backbone? I could not find a clear explanation of the issue and how to solve it.
To be precise, what should I add to the function I trigger when clicking on the tab of a section to be loaded with ajax? My aim is to change the URL and the page (go back to state before AJAX request) when using the Back button. I'm currently doing as follows:
RenderSection: function(event) {
var data = '';
var url = $(event.currentTarget).attr("href");
$.post(url, data, function(data){
$(".ajax_section").html(data);
var protocol = this.protocol + '//';
// Ensure the protocol is not part of URL, meaning its relative.
if (url && url.slice(protocol.length) !== protocol) {
Backbone.history.navigate(url, true);
}
});
return false;
},
Turns out I was not using Backbone correctly. Here is what I ended up using and it works great!
In my Backbone View, I created 2 methods: the first is triggered when a link (an anchor) with class="ajax_enabled" is clicked, while the second is triggered by a Backbone Events trigger included in the Router's action. The Backbone View methods look as follows:
events: {
'click a.ajax_enabled': 'NavigateToUrl'
}
initialize: function() {
EventAggregator.on("render:route", this.RenderAjax, this);
},
NavigateToUrl: function(event) {
var url = $(event.currentTarget).attr("href");
var protocol = this.protocol + '//';
// Ensure the protocol is not part of URL, meaning its relative.
if (url && url.slice(protocol.length) !== protocol) {
Backbone.history.navigate(url, true);
}
return false;
},
RenderAjax: function(route) {
var data = '';
var url = window.location.pathname + window.location.search;
$.post(url, data, function(data){
$(".ajax_section").html(data);
});
}
My Backbone Router handles the call from Backbone.history.navigate(url, true); and triggers the event to update the view through the default action, as follows:
window.EventAggregator = _.extend({}, Backbone.Events);
var Router = Backbone.Router.extend({
initialize: function(options) {
var router = this;
Backbone.history.start({pushState: true, root: "/", silent: true});
},
routes: {
'' : 'defaultAction',
'*route': 'defaultAction'
},
defaultAction: function(route) {
if(typeof(route)==='undefined') {
route = '';
}
EventAggregator.trigger("render:route", route);
}
});
return Router;
For reference, I found this other answer helpful, about using events to trigger methods in the View from the Router.

How to send data from server to client via http?

I want to send the filepath of a file on my server to the client in order to play it using a media player. How can I retrieve that string on the client side in order to concatenate it in the src attribute of a <video element without using sockets?
Server snippet:
res.set('content-type', 'text/plain');
res.send('/files/download.mp4');
This is how you make a request to the server without any frameworks. "/path_to_page" is the route you set to the page that is supposed to process the request.
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path_to_page', true);
xhr.onload = function(e) {
if (this.status == 200) {
console.log(this.responseText); // output will be "/files/download.mp4"
}
};
xhr.send();
}
You might also want to send some params.
var formdata = new FormData();
formdata.append("param_name", "value");
So you might for instance want to send the filename or such.
You just need to change 2 lines from the first code snippet. One would be
xhr.open('POST', '/path_to_page', true); // set to post to send the params
xhr.send(formdata); // send the params
To get the params on the server, if you are using express, they are in req.body.param_name
Which framework are you using??
You can declare base path of your project directory in ajax and the followed by your file.
jQuery.ajax({
type: "GET",
url: "/files/download.mp4",
});
Since you are using express (on node), you could use socket.io:
Server:
var io = require('socket.io').listen(80),
fs = require('fs');
io.sockets.on('connection', function (socket) {
socket.on('download', function(req) {
fs.readFile(req.path, function (err, data) {
if (err) throw err;
socket.emit('video', { video: data });
});
});
});
Client:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
...
// request a download
socket.emit('download', { path: '/files/download.mp4' });
// receive a download
socket.on('video', function (data) {
// do sth with data.video;
});
...
</script>
Edit: didnt notice you didnt want to use sockets. Still it is a viable solution.

Resources