SignalR ConnectionData empty, and won't connect - c#

I have two SignalR hubs in my MVC app, ChatHub and AnotherHub. ChatHub is working nicely, but I can't connect to the other one. I know that SignalR recognises that AnotherHub exists, because I'm getting the following JavaScript in /signalr/hubs:
signalR.chatHub = {
_: {
hubName: 'ChatHub',
ignoreMembers: ['post'],
connection: function () { return signalR.hub; }
},
post: function (room, message) {
return invoke(this, "Post", $.makeArray(arguments));
}
};
signalR.anotherHub = {
_: {
hubName: 'AnotherHub',
ignoreMembers: ['doSomething'],
connection: function () { return signalR.hub; }
},
doSomething: function (thing) {
return invoke(this, "DoSomething", $.makeArray(arguments));
}
};
On the chat page, Fiddler tells me the following when connecting with /signalr/signalr/connect:
connectionData [{"name":"chathub"}]
tid 10
However, when trying to connect to anotherHub, Fiddler says:
connectionData []
tid 3
My javascript on the chat page:
cn = $.connection.chatHub;
$.connection.hub.start();
and on the another page:
cn = $.connection.anotherHub;
$.connection.hub.start();
Now, a curious thing; when I change anotherHub to chatHub on the another page, the connection works. When I change chatHub to anotherHub on the chat page, that also works. The only combination that doesn't is anotherHub on the another page. (I've confirmed this by server-side breakpoints on the hubs' constructors and checking fiddler for the ConnectionData parameter). Any ideas?

Are you sure you have referenced all of the same JS libraries (especially /signalr/hubs) on the another page?

Related

SignalR in ReactJS page works just with reload

I use signalr in React.Js and have a problem that when I go to the pages by clicking the menu link and redirect with Route, it is not working. The back-end singleton class constructor is not called, but when I refresh a page or type the route manually in the address bar, it is works and connects to the back-end.
import $ from "jquery";
window.jQuery = $;
require("signalr");
{componentDidMount() {
var connection = $.hubConnection("http://localhost:1425/");
proxy = connection.createHubProxy("myHub");
connection
.start({ withCredentials: false })
.done(function () {
console.log("Now connected, connection ID=" + connection.id);
})
.fail(function () {
console.log("Could not connect");
});
}
Why does this happen and can anyone guide me on how should I fix that?

ADFS login error when Session variables used in MVC

I am getting this error when I use Session variable and I do the following:
1) Log in via ADFS and access the application
2) Do operations
3) Close all browser sessions. (Happens both in IE and Chrome)
4) I try to log in via ADFS again and it redirects about 4 times and it throws the error.
IIS has to be restarted to get the application working since it affects the application globally. None of the users can access it once this error occurs.
I narrowed it down to Session variables, even if I use TempData, ViewBag, ViewData it gives the error. If I remove the Session variables it works, it doesn't give me the error.
Any ideas why Session Variables are causing the error?
[HttpPost]
[ValidateAntiForgeryToken]
//Gets called on button clicked
public ActionResult SaveDoc()
{
Session["myList"] = bpc.UploadDocument(dto);
return Json(new { success = true, redirectToUrl = Url.Action("Summary") });
}
public ActionResult Summary()
{
return View();
}
[HttpPost]
public ActionResult GetResults_List([DataSourceRequest]DataSourceRequest request, CloudDTO dto)
{
var getResults = (List<SampleList>)Session["myList"];
return Json(getResults.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
Client Side
function btnSubmitClick() {
displaySpinner(true);
$.ajax({
url: '#Url.Action("SaveDoc", "Home")',
datatype: 'json',
data: {
__RequestVerificationToken: $('[name=__RequestVerificationToken]').val(),
URL: value,
CloudProvider: $("#cloudProvider").data("kendoDropDownList").text(),
},
type: "POST"
}).success(function (data) {
if (data.success) {
//On success we go to the Summary page.
window.location.href = data.redirectToUrl;
}
else {
//Error Message to user.
$("#validationMessage").html(data.message);
}
})
.done(function () {
displaySpinner(false);
});
I was having the exact same issue and I also realised it was down to the use of Session Variables.
The reason it is happening is because the Session Variables interfere with the cookie set by the Owin response headers. The workaround to this issue is to create a custom cookie manager. This resolved the issue for me
This Microsoft article explains the issue and also provides code samples for creating a custom cookie manager: https://blogs.msdn.microsoft.com/dsnotes/2016/08/25/owin-cookies-signin-error-with-ad-fs/

Database Change Notifications in ASP.NET using SignalR and SqlDependency

I'm a serious MVC & SignalR newbie!
I found this tutorial online that shows how to use Database Change notifications and display the data in an MVC app. The issue I'm having is replicating the MVC side of things. I've managed I think to work through the tutorial correctly and spin up the application, however, I'm getting an Undefined connection and the app bombs out.
Does anyone know where I might find the associated source files for this example, or has anyone managed to successfully implement this and can shed some light on the configuration of this from an MVC point of view.
Thanks!
To display real time updates from the SQL Server by using SignalR and SQL Dependency I've done these steps:
Step 1: Enable Service Broker on the database
The following is the query that need to enable the service broker
ALTER DATABASE BlogDemos SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;
Step 2: Enable SQL Dependency
//Start SqlDependency with application initialization
SqlDependency.Start(connString);
Step 3: Create the hub Class
public class MessagesHub : Hub
{
private static string conString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
public void Hello()
{
Clients.All.hello();
}
[HubMethodName("sendMessages")]
public static void SendMessages()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessagesHub>();
context.Clients.All.updateMessages();
}
}
Step 4: Get the Data from the Repository
Create MessagesRepository to get the messages from the database when data is updated.
public class MessagesRepository
{
readonly string _connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
public IEnumerable<Messages> GetAllMessages()
{
var messages = new List<Messages>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [MessageID], [Message], [EmptyMessage], [Date] FROM [dbo].[Messages]", connection))
{
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
messages.Add(item: new Messages { MessageID = (int)reader["MessageID"], Message = (string)reader["Message"], EmptyMessage = reader["EmptyMessage"] != DBNull.Value ? (string) reader["EmptyMessage"] : "", MessageDate = Convert.ToDateTime(reader["Date"]) });
}
}
}
return messages;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
MessagesHub.SendMessages();
}
}
}
Step 5: Register SignalR at startup class
app.MapSignalR();
Step 6: then use the method to show real time at your view
<script src="/Scripts/jquery.signalR-2.1.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.messagesHub;
//debugger;
// Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function () {
getAllMessages()
};
// Start the connection.
$.connection.hub.start().done(function () {
alert("connection started")
getAllMessages();
}).fail(function (e) {
alert(e);
});
});
function getAllMessages()
{
var tbl = $('#messagesTable');
$.ajax({
url: '/home/GetMessages',
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
</script>
Hope this helps :)
You should provide your code, it's easier to figure out what the problem is that way.
From what you have mentioned, I can think of only two things.
1) The tutorial you used, it's using SignalR 1.0. If you are using SignalR 2.0, you should not follow the tutorial exactly.
A few things changed in SignalR 2.0, you can read about it using below link:
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/upgrading-signalr-1x-projects-to-20
2) If you are using SignalR 1.0, make sure you include the "jquery" file correctly, and if it's correct, try to change the hub proxy path like below:
<script src="/signalr/Hubs"></script>
Hope it helps

session timeout on ajax call

I know this is duplicate but I could not get reliable solution(for asp.net web).
I just want to redirect to the login page if session expires.
I have tried following:
1. using jquery status code
$.ajax({
type: "POST",
url: "stream.asmx/SomeMethod",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
//success msg
},
error: function (request, status, error) {
if (status = 403) {
location.href = 'login.aspx';
}
}
});
Problem: this returns same status code(403) for other errors too, which I only expect for session timeout.
2. Sending json message whether session expired
code behind:
if (!object.Equals(HttpContext.Current.Session["User"], null))
{
Id = int.Parse(HttpContext.Current.Session["User"].ToString());
}
else
{
result = from row in dtscrab.AsEnumerable()
select new
{
redirectUrl = "login.aspx",
isRedirect = true
};
}
on $.ajax success:
success: function (msg) {
if (msg.d[0].isRedirect) {
window.location.href = msg.d[0].redirectUrl;
}
else {
//load containt
}
}
Problem: It's somehow desn't invoke ajax success line if session expires(it does return correct json). And even this is not a proper way if I have many number of ajax request in the page(should be handled globally).
However, I saw this post which is really good soltion but it's for mvc using AuthorizeAttribute: handling-session-timeout-in-ajax-calls
So, Is there I can use same concept used in mvc using AuthorizeAttribute in asp.net web api? If not, how I can troubleshoot those issue which I'm facing (any of above two mentioned)?
A 403 status code is going to cause jQuery to call the failure method. Keep the same code behind from your second try, but move the redirect handler to the failure method instead of the success method. In the success method, treat it as you normally would.
Problem:
I had same problem in my Razor MVC Application throwing exceptions while ajax calls made when session timed out.
The way I have managed to get this issue sorted is by monitoring each ajax requests by using a simple light weight Action Method (RAZOR MVC) returning a bool variable whether the Request is Authenticated or not. Please find the code below..
Layout/Master Page / Script file:
<script>
var AuthenticationUrl = '/Home/GetRequestAuthentication';
var RedirectUrl = '/Account/Logon';
function SetAuthenticationURL(url) {
AuthenticationUrl = url;
}
function RedirectToLoginPage() {
window.location = RedirectUrl;
}
$(document).ajaxStart(function () {
$.ajax({
url: AuthenticationUrl,
type: "GET",
success: function (result) {
if (result == false) {
alert("Your Session has expired.Please wait while redirecting you to login page.");
setTimeout('RedirectToLoginPage()', 1000);
}
},
error: function (data) { debugger; }
});
})
Then in Home Controller/Server side you need a method to verify the request and return the boolean variable..
public ActionResult GetAuthentication ( )
{
return Json(Request.IsAuthenticated, JsonRequestBehavior.AllowGet);
}
This will validate each ajax request and if the session got expired for any ajax request, it will alert the user with a message and redirect the user to the login page.
I would also suggest not to use standard Alert to Alert. User some Tool tip kind of formatted div Alerts. Standard JS Alerts might force the user to click OK before redirection.
Hope it helps.. :)
Thanks,
Riyaz
Finally, I ended up following.
public class IsAuthorizedAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var sessions = filterContext.HttpContext.Session;
if (sessions["User"] != null)
{
return;
}
else
{
filterContext.Result = new JsonResult
{
Data = new
{
status = "401"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
//xhr status code 401 to redirect
filterContext.HttpContext.Response.StatusCode = 401;
return;
}
}
var session = filterContext.HttpContext.Session;
if (session["User"] != null)
return;
//Redirect to login page.
var redirectTarget = new RouteValueDictionary { { "action", "LogOn" }, { "controller", "Account" } };
filterContext.Result = new RedirectToRouteResult(redirectTarget);
}
}
Handling client side
<script type="text/javascript">
$(document).ajaxComplete(
function (event, xhr, settings) {
if (xhr.status == 401) {
window.location.href = "/Account/LogOn";
}
});
</script>
you can set session time out expire warning some thing like ....
<script type="text/javascript">
//get a hold of the timers
var iddleTimeoutWarning = null;
var iddleTimeout = null;
//this function will automatically be called by ASP.NET AJAX when page is loaded and partial postbacks complete
function pageLoad() {
//clear out any old timers from previous postbacks
if (iddleTimeoutWarning != null)
clearTimeout(iddleTimeoutWarning);
if (iddleTimeout != null)
clearTimeout(iddleTimeout);
//read time from web.config
var millisecTimeOutWarning = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeoutWarning"]) * 60 * 1000 %>;
var millisecTimeOut = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeout"]) * 60 * 1000 %>;
//set a timeout to display warning if user has been inactive
iddleTimeoutWarning = setTimeout("DisplayIddleWarning()", millisecTimeOutWarning);
iddleTimeout = setTimeout("TimeoutPage()", millisecTimeOut);
}
function DisplayIddleWarning() {
alert("Your session is about to expire due to inactivity.");
}
function TimeoutPage() {
//refresh page for this sample, we could redirect to another page that has code to clear out session variables
location.reload();
}
4xx are HTTP error status codes and would cause jquery to execute the onFailure callback.
Also, beware of using 3xx for redirects when you want to process the payload. Internet Explorer, in my experience, just does a redirect (without looking at the payload) when a 3xx status code is returned.
I'd say, throw a 403 and handle the situation. To the client 403 implies the resource access is forbidden. There can be multiple reasons, which is OK I guess.
For those using a ScriptManager, you can easily check for ajax request and then redirect with the following code:
private void AjaxRedirect(string url)
{
Response.StatusCode = 200;
Response.RedirectLocation = url;
Response.Write("<html></html>");
Response.End();
}
Then check for request type and redirect accordingly (using routes here):
if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
{
var redirectUrl = RouteTable.Routes.GetVirtualPath(null, "Default", null).VirtualPath;
AjaxRedirect(redirectUrl);
}
else
{
Response.RedirectToRoute("Default");
}
The "Default" route is a route defined in the routes collection:
routes.MapPageRouteWithName("Default", "", "~/default.aspx");
If you prefer, instead of using ScriptManager for ajax request check, you can use:
if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") {
code here...
}

AJAX error in ASP.NET c#

I am very new to Ajax and ASP.NET MVC. I have a function, that returns back to AJAX and I need to handle the error situation. When everything works fine, then the code is okay. My question is how to handle the error part. Here is what I have:
To return success I have:
var data = new { success = false };
return Json(data, JsonRequestBehavior.AllowGet);
And I need to know what to return when there is an exception or error??
This is my query:
function DoMailPDF() {
$("#submitMail").attr("disabled", true);
var personid = $("#personid").val();
var unitid = $("#unitid").val();
var url = "#(Url.Action("SendEmail", "Report"))";
$.ajax({
url: url,
data: { person: personid , unit:unitid},
success: function () {
// $('input[name=MailSent]').attr('checked', true);
$("#submitMail").removeAttr("disabled");
alert("Email sent!");
},
error: function () {
alert("Email not sent!");
}
});
}
It never comes to the error function. How do I make it go to the error? Any tips and suggestions are most welcome.
You can access your json response object by writing:
$.ajax({
url: url,
data: { person: personid , unit:unitid},
dataType: 'json',
success: function (response) {
if (response.success == false) {
// Error handling
} else {
// Success handling
}
},
error: function () {
alert("Email not sent!");
}
});
As Nick Bork already explained in a comment, the error/success status of a response is determined by the Http status code that is sent down in the header. You can still go the suggested way and inspect the response object and the success property but it is clearly not the proper way when you already have a more powerful and long proven mechanism (the HTTP protocol itself).
.NET will use HTTP 200 (OK) when everything goes according to the code but you can change this behaviour in the Controller by accessing the Reponse object like this, for example:
Response.StatusCode = 500; // or any of the other HTTP "failure" status codes
Any status code in the 4xx or 5xx category will trigger the error() handler specified in the $.ajax(...) call. From there you can of course also inspect the proper status code, the response details and every properties of the XHR object to provide a more meaningful user experience.
HTTP status codes are pretty much set in stone and are not likely to change, that's why they are in my opinion definitely preferrable to a custom made solution...
PS: For a list of HTTP status codes, wikipedia is your friend.

Categories

Resources