signalR maintaining user connection ids - asp.net-mvc-3

I am trying to maintain a connection id for the user ,i mean even he refreshes the page he gets the same connectionid
This is what i could do till now
the javascript part
// Start the connection
$.connection.hub.start(function () { chat.join(projectId, userId, userName); }).done(function () {
alert("Connected!");
var myClientId = $.connection.hub.id;
setCookie("srConnectionid", myClientId, 1);
});
function setCookie(cName, value, exdays) {
try{
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate);
document.cookie = cName + "=" + c_value;
}
catch(err){
alert(err.Description);
}
}
and then i made a class that inherits IConnectionIdFactory like this
public class MyConnectionFactory : IConnectionIdFactory
{
public string CreateConnectionId(IRequest request)
{
if (request.Cookies["srconnectionid"] != null)
{
return request.Cookies["srconnectionid"];
}
return Guid.NewGuid().ToString();
}
}
i registerd the above class in Application_start() as below
protected void Application_Start()
{
AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new MyConnectionFactory());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
My problem is everytime MyConnectionFactory class is called inside the CreateConnectionId
request.Cookies["srconnectionid"] is null everytime,so the user is assigned new connectionid everytime.I could find only one link that helped me to maintain connection ids.It is http://www.kevgriffin.com/maintaining-signalr-connectionids-across-page-instances
Can any one suggest how to fix my problem or is there any other approach to reuse the connection id for the same user...?
The Cookie value is set in the clientside.I have been trying this for 2 days.It would be great help
Thanks in advance

The expire date for the cookie should be UTC string (you are not doing this, so most probably the server is treating your cookie as expired). Change your code like this:
var cValue = escape(value) + ((exdays==null) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = cName + "=" + cValue;
Or you might just use jQuery Cookie plugin for setting the cookie.
UPDATE
Also the name of cookie is inconsitent in the code you have provided. You are setting the cookie with name 'userConnectionid' but trying to access by name 'srconnectionid'. Please check if you haven't made a spelling error there.

Make sure you set the cookie's path, especially if the page is sitting in a subdirectory. I'm following a similar approach, and when the cookie path is set to root, then it all works.

Related

How to make auto logout page in mvc after a some time

Title- asp.net-mvc5 Auto logout How to make form auto logout after sometime in asp.net-mvc5 and redirect automatically to login page
You need to create a session variable on the Login method.
The session will be created by Session["Userid"]=Userid;. Then you need to create custom attribute to check session timeout.
Steps you need to follow are:
Create a session variable in login() (Post method)
Create a class file in your MVC project.
Copy and paste below code in that file.
public class SessionTimeOutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Controller controller = filterContext.Controller as Controller;
HttpContext httpContext = HttpContext.Current;
var rd = httpContext.Request.RequestContext.RouteData;
string currentAction = rd.GetRequiredString("action");
string currentController = rd.GetRequiredString("controller");
if (HttpContext.Current.Session["UserId"] == null)
{
filterContext.Result = new RedirectResult("~/Account/Login?ReturnUrl=" + currentController + "/" + currentAction);
return;
}
base.OnActionExecuting(filterContext);
}
}
add [SessionTimeOut] attribute on each controller.
[SessionTimeOut]
public class ControllerName : Controller
{
You should add Statup.cs file.
1. Add Statup Class your project from new item lists.
2. Add following line in ConfigureService.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting =
false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddAuthorization();
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// we do this because we trust the network
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(x =>
{
x.Cookie.Name = "WriteSomeThings";
x.Cookie.SecurePolicy = CookieSecurePolicy.Always;
x.Cookie.SameSite = SameSiteMode.Strict;
x.Cookie.HttpOnly = true;
x.Cookie.IsEssential = true;
x.SlidingExpiration = true;
x.ExpireTimeSpan = TimeSpan.FromHours(8);//For Auto Logout
x.LoginPath = "/User/LogOn";
x.LogoutPath = "/User/LogOff";
x.AccessDeniedPath = "/Home/AccessDenied";
});
}
x.ExpireTimeSpan = TimeSpan.FromHours(8) => This line allow us to logout automatically after 8 hours.
If you need full user management check this video
https://youtu.be/912q3TEF25U
Software development template with role-based user management using ASP.NET MVC 5. Try it for free

The route parameter with '/' is decoded before it is passed to the controller [duplicate]

Question:
I am creating a wiki software, basically a clone of wikipedia/mediawiki, but in ASP.NET MVC (the MVC is the point, so don't recommend me ScrewTurn).
Now I have a question:
I use this route mapping, to route a URL like:
http://en.wikipedia.org/wiki/ASP.NET
routes.MapRoute(
"Wiki", // Routenname
//"{controller}/{action}/{id}", // URL mit Parametern
"wiki/{id}", // URL mit Parametern
new { controller = "Wiki", action = "dbLookup", id = UrlParameter.Optional } // Parameterstandardwerte
);
Now it just occured to me, that there might be titles like 'AS/400':
http://en.wikipedia.org/wiki/AS/400
Incidentially, there is also this one (title 'Slash'):
http://en.wikipedia.org/wiki//
And this one:
http://en.wikipedia.org/wiki//dev/null
Overall, Wikipedia seems to have a list of interesting titles like this:
http://en.wikipedia.org/wiki/Wikipedia:Articles_with_slashes_in_title
How do I make routes like this route correctly ?
Edit:
Something like:
If the URL starts with /Wiki/, and if it doesn't start with /wiki/Edit/
(but not /Wiki/Edit)
then pass all the rest of the URL as Id.
Edit:
Hmm, just another problem:
How can I route this one:
http://en.wikipedia.org/wiki/C&A
Wikipedia can...
Edit:
According to wikipedia, due to clashes with wikitext syntax, only the following characters can never be used in page titles (nor are they supported by DISPLAYTITLE):
# < > [ ] | { }
http://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_(technical_restrictions)#Forbidden_characters
Edit:
To allow * and &, put
<httpRuntime requestPathInvalidCharacters="" />
into section <system.web> in file web.config
(Found here: http://www.christophercrooker.com/use-any-characters-you-want-in-your-urls-with-aspnet-4-and-iis)
You could use a catchall route to capture everything that follows the wiki part of the url into the id token:
routes.MapRoute(
"Wiki",
"wiki/{*id}",
new { controller = "Wiki", action = "DbLookup", id = UrlParameter.Optional }
);
Now if you have the following request: /wiki/AS/400 it will map to the following action on the Wiki controller:
public ActionResult DbLookup(string id)
{
// id will equal AS/400 here
...
}
As far as /wiki// is concerned I believe you will get a 400 Bad Request error from the web server before this request ever reaches the ASP.NET pipeline. You may checkout the following blog post.
in Attribute Routing in mvc i had the same problem having / in string abc/cde in HttpGet
[Route("verifytoken/{*token}")]
[AllowAnonymous]
[HttpGet]
public ActionResult VerifyToken(string token)
{
//logic here
}
so you have to place * because after this it will be considered as parameter
#Darin: Well, that much is obvious, the question is: Why ? controller
+ action + id are given, it's like it's passing all these to routing again... – Quandary Jun 13 '11 at 17:38
Quandry - maybe you have already figured this out since your question is over a year old, but when you call RedirectToAction, you are actually sending an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. Hence, the infinite loop you are seeing.
See: Controller.RedirectToAction Method
Still as an option write in the file Global.asax:
var uri = Context.Request.Url.ToString();
if (UriHasRedundantSlashes(uri))
{
var correctUri = RemoveRedundantSlashes(uri);
Response.RedirectPermanent(correctUri);
}
}
private string RemoveRedundantSlashes(string uri)
{
const string http = "http://";
const string https = "https://";
string prefix = string.Empty;
if (uri.Contains(http))
{
uri = uri.Replace(http, string.Empty);
prefix = http;
}
else if (uri.Contains(https))
{
uri = uri.Replace(https, string.Empty);
prefix = https;
}
while (uri.Contains("//"))
{
uri = uri.Replace("//", "/");
}
if (!string.IsNullOrEmpty(prefix))
{
return prefix + uri;
}
return uri;
}
private bool UriHasRedundantSlashes(string uri)
{
const string http = "http://";
const string https = "https://";
if (uri.Contains(http))
{
uri = uri.Replace(http, string.Empty);
}
else if (uri.Contains(https))
{
uri = uri.Replace(https, string.Empty);
}
return uri.Contains("//");
}

Session variable value is getting null in ASP.NET Core

I am setting a session variable in one method and trying to get the session variable value from the another method in a controller but its always getting null:
Here is my code:
public class HomeController : Controller
{
public IActionResult Index()
{
HttpContext.Session.SetString("Test", "Hello!");
var message = HttpContext.Session.GetString("Test");// Here value is getting correctly
return View();
}
public IActionResult About()
{
var message = HttpContext.Session.GetString("Test"); // This value is always getting null here
return View();
}
}
Here is my session configuration in Startup class:
In ConfigureServices() method:
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDistributedMemoryCache();
services.AddMvc().AddSessionStateTempDataProvider();
services.AddSession(options =>
{
options.Cookie.Name = "TanvirArjel.Session";
options.IdleTimeout = TimeSpan.FromDays(1);
});
In Configure() method:
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Very strange and peculiar problem! Any help will be highly appreciated!
For ASP.NET Core 2.1 and 2.2
In the ConfigureServices method of the Startup class, Set options.CheckConsentNeeded = context => false; as follows:
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
Problem solved!
You can also just set Cookie.IsEssential = true as explained here: https://andrewlock.net/session-state-gdpr-and-non-essential-cookies/
There is an overload of services.AddSession() that allows you to configure SessionOptions in your Startup file. You can change various settings such as session timeout, and you can also customise the session cookie. To mark the cookie as essential, set IsEssential to true:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true; // consent required
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSession(opts =>
{
opts.Cookie.IsEssential = true; // make the session cookie Essential
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
I have had the same issue and tried the following separately and I found that either of them does work for me!
1. options.CheckConsentNeeded = context => false;
2. opts.Cookie.IsEssential = true; // make the session cookie Essential
However, not quite sure though, I think #1 might potentially lead to the breach of GDPR. Hence I would prefer #2.
I tried adding the mentioned lines from Microsoft docs on session it didn't work.
As I see from the code of HttpContext.Session Session can be used in the same request.
So at last, I created one static class as below which helped as Session throughout the application.
public static class SessionHelper
{
private static IDictionary<string, string> Session { get; set; } = new Dictionary<string, string>();
public static void setString(string key, string value)
{
Session[key] = value;
}
public static string getString(string key)
{
return Session.ContainsKey(key) ? Session[key] : string.Empty;
}
}
Usage:
set string in one request
SessionHelper.setString("UserId", "123");
read string in another request
SessionHelper.getString("UserId");
Hope it helps!

ServiceStack caching strategy

I'm learning ServiceStack and have a question about how to use the [Route] tag with caching. Here's my code:
[Route("/applicationusers")]
[Route("/applicationusers/{Id}")]
public class ApplicationUsers : IReturn<ApplicationUserResponse>
{
public int Id { get; set; }
}
public object Get(ApplicationUsers request)
{
//var cacheKey = UrnId.Create<ApplicationUsers>("users");
//return RequestContext.ToOptimizedResultUsingCache(base.Cache, cacheKey, () =>
return new ApplicationUserResponse
{
ApplicationUsers = (request.Id == 0)
? Db.Select<ApplicationUser>()
: Db.Select<ApplicationUser>("Id = {0}", request.Id)
};
}
What I want is for the "ApplicationUsers" collection to be cached, and the times when I pass in an Id, for it to use the main cached collection to get the individual object out.
If I uncomment the code above, the main collection is cached under the "users" key, but any specific query I submit hits the Db again. Am I just thinking about the cache wrong?
Thanks in advance,
Mike
this line
var cacheKey = UrnId.Create<ApplicationUsers>("users");
is creating the same cache key for all the requests, you must use some of the request parameters to make a "unique key" for each different response.
var cacheKey = UrnId.Create<ApplicationUsers>(request.Id.ToString());
this will give you the "urn:ApplicationUsers:0" key for the get all and the "urn:ApplicationUsers:9" for the request with Id = 9
now you can use the extension method in this way.
return RequestContext.ToOptimizedResultUsingCache(Cache, cacheKey, () => {
if(request.Id == 0) return GetAll();
else return GetOne(request.Id);
});
I hope this helps, regards.

ASP.NET MVC language change link

I have an ASP.NET MVC site that it's in two languages using Resources. To allow the server to present the site in the apropiate language (depending on the one that's configured in the user's browser) I put the following in my web.config:
<globalization culture="es-es" uiCulture="auto" />
How can I add a link to change the uiCulture? I want to store the selection in a cookie and if it's not present, then fall back to the browser configuration... Is it possible?
You may take a look at the following guide. It uses Session to store the current user language preference but the code could be very easily tweaked in order to use a cookie. The idea is that you will have a controller action:
public ActionResult ChangeCulture(string lang, string returnUrl)
{
var langCookie = new HttpCookie("lang", lang)
{
HttpOnly = true
};
Response.AppendCookie(langCookie);
return Redirect(returnUrl);
}
and then in Global.asax you could subscribe for the Application_AcquireRequestState event in order to set the current thread culture based on the value of the cookie:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
var langCookie = HttpContext.Current.Request.Cookies["lang"];
if (langCookie != null)
{
var ci = new CultureInfo(langCookie.Value);
//Checking first if there is no value in session
//and set default language
//this can happen for first user's request
if (ci == null)
{
//Sets default culture to english invariant
string langName = "en";
//Try to get values from Accept lang HTTP header
if (HttpContext.Current.Request.UserLanguages != null && HttpContext.Current.Request.UserLanguages.Length != 0)
{
//Gets accepted list
langName = HttpContext.Current.Request.UserLanguages[0].Substring(0, 2);
}
langCookie = new HttpCookie("lang", langName)
{
HttpOnly = true
};
HttpContext.Current.Response.AppendCookie(langCookie);
}
//Finally setting culture for each request
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = ci;
//The line below creates issue when using default culture values for other
//cultures for ex: NumericSepratore.
//Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
}
}
Now this being said using cookies and session to store current language is not SEO friendly. What I prefer doing when I need a localized application is to use a special route which will contain the language:
routes.MapRoute(
"Default",
"{lang}/{controller}/{action}/{id}",
new
{
lang = "en-US",
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);
and then prefix all my urls with the language. This provides unique urls for different languages so that robots can properly index all content. Now all that's left is to modify the Application_AcquireRequestState method so that it uses the lang token of the url and based on its value set the proper Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture.
And now when you wanted to change the language you would simply generate the proper link:
#Html.ActionLink("Page index en français", "index", new { lang = "fr-FR" })
An alternative and I feel it is more flexible
protected override void ExecuteCore()
{
if (RouteData.Values["lang"] != null && !string.IsNullOrWhiteSpace(RouteData.Values["lang"].ToString()))
{
SetCulture(RouteData.Values["lang"].ToString());
}
else
{
var cookie = HttpContext.Request.Cookies["myappculture"];
if (cookie != null)
{ SetCulture(cookie.Value); }
else
{ SetCulture(HttpContext.Request.UserLanguages[0]);}
}
base.ExecuteCore();
}
public ActionResult ChangeCulture(string lang, string returnUrl)
{
SetCulture(lang);
// Little house keeping
Regex re = new Regex("^/\\w{2,3}(-\\w{2})?");
returnUrl = re.Replace(returnUrl,"/" + lang.ToLower());
return Redirect(returnUrl);
}
private void SetCulture(string lang)
{
CultureInfo ci = new CultureInfo(lang);
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
// Force a valid culture in the URL
RouteData.Values["lang"] = lang;
// save the location into cookie
HttpCookie _cookie = new HttpCookie("myappculture", Thread.CurrentThread.CurrentUICulture.Name);
_cookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Response.SetCookie(_cookie);
}
In the view
I kept the resource in a different project as follows
If you use the App_GloabalResources to store your resx language files, all you have to do is add a drop down which changes the current thread's UI Culture and this will automatically select the right resx language file to display.
App_GloabalResources is not the right place the resources when it comes to MVC programmering. See http://buildingwebapps.blogspot.no/2012/05/right-way-to-use-resource-files-for.html

Resources