How can I apply new locale in MVC application ?
I've created an Action that sets
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
then redirects to Index.chtml , but this is not working.
How to make this thing work please ?
Setting the culture is only valid until the thread terminates, which happens after all page processing has finished. In this case, after you issue the redirect the server will send the Location HTTP header to the new address and close the response.
The browser will then initiate a new request to the new location and the value you set in Thread.CurrentThread.CurrentCulture will reset to the default one.
You have to persist the language selection (session, cookie...) and then apply it at start of your page logic.
Related
I can't share my own app for nda issue but I found this one which shows exactly the same issue
https://github.com/SyncfusionExamples/blazor-localization/tree/master/.NET6%20Blazor%20Server%20App/Localization-with-dynamic-culture
Blazor-Localization
This app displays a page with a CultureSwitcher component to select between 5 languages.
When you select a language, I can see both the page+dropdown refreshed with the selected language value.
Now, you embed the Url of this app inside a new app's page inside an iFrame and nothing works anymore.
The cookie mechanism doesn't save or/and load the value anymore.
This was working perfectly in net+core+3.1 and 5.0 but no more in net+core+6.
Is something change in security or cors or ????
Thanks in advance for any tips
-Vince
This sounds like an issue with SameSite cookies. It's even documented here:
Applications that use <iframe> may experience issues with sameSite=Lax
or sameSite=Strict cookies because is treated as cross-site
scenarios.
Have you tried setting the cookie options?
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(
new RequestCulture(culture)),
new CookieOptions() { SameSite = SameSiteMode.None, Secure = true} );
In our ASP.NET Core web app, I need to be able to access HttpContext.Session within a Custom RequestCultureProvider I've written. There is alot of background here... To be brief, I'll just mention that the business requirement is that I'm trying to meet, is to be able to display data in at least two different cultures, in our web app. A user may navigate to one page, where it should display in fr-FR and navigate to another page where it should display in zh-CN.
I have all of this working, when it comes to the "Response" from the server. By using an ActionFilter on a per-Action basis I can set the current thread's culture to whatever I want. We look up the various cultures from a database and store them in Session and then n the ActionFilter, based on an argument I pass to the ActionFilter method... I can even load ViewComponents via Controller Actions in the various cultures and have each view component display with a different culture setting. Given this, you can see why I can't store session in a Cookie or use the Querystring.
But the PROBLEM comes into play with "Requests" to the server, like when I edit Form data in a view and Submit/post the data back to an Action.
When I do that, my Dates are sent back in as NULL. BTW, I do Model binding and I have my dates marked with this attributes in my ViewModels:
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MMM-yyyy}")]
According to the well written article below, the order in which the Request culture is looked up, is:
https://www.jerriepelser.com/blog/how-aspnet5-determines-culture-info-for-localization/
From the query string
From a cookie
From the Accept-Language HTTP header
From the DefaultRequestCulture property of the RequestLocalizationOptions class
From the thread culture
I can certainly verify the above. If I use a cookie, it works. If I add French fr-FR as the first language in Chrome Settings, it works. If I change my default request culture in my Startup localization to fr-FR, it works...
For example, in the web page a Kendo DatePicker (or even an tag) will contain a French date like "14-août-2019", yet when I post it back to an Action, it is NULL unless I've set the querystring, cookie, Accept-Language or DefaultRequestCulture to fr-FR.
One odd thing is that #5 says it should use the current Thread Culture, and in my Action that I post Form data to I check the current thread's culture, and it is indeed fr-FR, so I don't know why my french dates are not being recognized...
So what I tried to get around this was to clear out all of those default providers and write a custom provider.
In Startup.ConfigureServices, I now do this:
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("fr-FR"),
new CultureInfo("zh-CN"),
new CultureInfo("en-IE"),
};
var defaultCultureSettingOverride = this.Configuration.GetSection("appsettings").GetValue<string>("DefaultCultureOverride");
defaultCultureSettingOverride = defaultCultureSettingOverride == null ? "en-US" : defaultCultureSettingOverride;
options.DefaultRequestCulture = new RequestCulture(defaultCultureSettingOverride);
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders.Clear();
options.RequestCultureProviders.Insert(0, new MyCustomRequestCultureProvider()
{
Options = options,
});
});
And that works too, as long as my MyCustomRequestCultureProvider returns the correct culture!
My problem now is: I NEED to be able to access Session state in my custom RequestCultureProvider. But the HttpContext sent into DetermineProviderCultureResult has a null Session. Why? Is it the wrong place in the pipeline?
And when I call options.RequestCultureProviders.Insert(0, , new MyCustomRequestCultureProvider() in ConfigureServices, I don't have access yet (as far as I can tell) to the HttpContextAccessor.
If I pass new MyCustomRequestCultureProvider(new HttpContextAccessor()), that does me no good...
And while I have access to IHttpContextAccessor accessor as a parameter passed into Startup.Configure, I can't do the same thing with Startup.ConfigureServices. If I add (IHttpContextAccessor accessor) as an additional parameter to ConfigureServices, .NET Core errors when I run it, telling me that method can only access an IServicesCollection parameter.
I'm at a dead end...
Well, Session being NULL in my custom RequestCultureProvider was my fault.
In Startup.Configure, we had app.UseSession() AFTER app.UseRequestLocalization()
Once I moved app.UseSession() BEFORE app.UseRequestLocalization(), I was able to access Session in my custom RequestCultureProvider.
And now once I get the user's culture from Session and use it to return the proper ProviderCultureResult, my form posts do contain the dates.
I am using asp.net core 3.1 Identity and set the ConfigureApplicationCookie'sExpireTimeSpan = TimeSpan.FromMinutes(300);
and in Login.cshtml.cs
var authenticationProperties = new AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true
};
await _signInManger.SignInWithClaimsAsync(user, authenticationProperties, claims.AsEnumerable());
but it still auto-logoff when there is 15 minutes'inactive.
and I have insert an iframe to the page and reload the iframe every 1 minutes but it still not work.
how to fix it?
I do not want it to log off while some minutes I do nothing in the Page.
Thats the expected behavior. Since you do nothing in the page the server is not being contacted and the cookie is not being refreshed. One work around to achieve your desired result (to not deactivate when the page is open and no action is taken) is to create a controller endpoint to ping at.
public void RefreshCookie(){}
Then you will need to call this action from the a continuous running js function
$.ajax({url:your_url/RefreshCookie}) // I dont quite remember the syntax
Thats the general idea. Remember if you are just standing still on a page or if you have closed your PC does not matter for the server as long as there is no communication between them.
The problem I'd like to solve is the following:
I need to manage an external web page through PHP, for example, login and then change the profile info on the external web after sending an ajax request on my own web.
For this, I'm calling PhantomJS from PHP to do those tasks, but before login to the external web I need to fill the captcha input. So, I'd like to send back the Captcha image to my web, write the correct code and send it back to the WebPage module of PhantomJS to login using that code.
In other words, I need a 'syncronous' program like this:
1) PHP -> Send a request to login and obtain the captcha image.
2) PhantomJS -> Open a WebPage instance and render the captcha code to an image.
3) PHP -> Get the captcha image, show it to an user and send a text input to PhantomJS.
4) PhantomJS -> Get the text code from PHP, fill the captcha input using 'page.evaluate' and login. Send to PHP some data ('Login successfull', 'Login failed', etc)
5) PHP -> Get the callback and send another task or data.
callback = 'Login successfull' --> Change profile picture or update user info.
callback = 'Login failed' --> Try to login again (like point 1)
Etc...
There are many things I don't know how to handle. For example:
1) How could I keep the WebPage module open and waiting for the text code of the captcha? If I close it, a new captcha code will appear next time, and I need a way to wait the code and get it. Do I need to start a server for this?
2)Get the captcha image from PHP isn't a problem (because of 'page.render'), but how I could send a text back to the WebPage instance of PhantomJS? I think is better to send data bidirectionally between both systems. Again, do I need a server?
I think I need a socket server in PhantomJS (how can this be done?). This server should have the WebPage instance that I need to keep open, but I'm not completely sure about this.
Thanks.
I recently published a project that gives PHP access to a browser. Get it here: https://github.com/merlinthemagic/MTS, Under the hood is an instance of PhantomJS.
The main issue is keeping a resource alive after initial execution. Here is how i propose you do it.
After downloading and setup you would simply use the following code:
Start of "Setup" session:
if (isset($_POST['sessionUID']) === false) {
//set the execution timeout long enough to cover the entire process (setup and working time), it dictates when phantomJS shuts down automatically.
ini_set('max_execution_time', 300);
//open the login page:
$myUrl = "http://www.example.com";
$browserObj = \MTS\Factories::getDevices()->getLocalHost()->getBrowser('phantomjs');
//allow the page to live after php shuts down.
$browserObj->setKeepalive(true);
$windowObj = $browserObj->getNewWindow($myUrl);
//find the username input box, here it has id=username
$windowObj->mouseEventOnElement("[id=username]", 'leftclick');
//type your username
$windowObj->sendKeyPresses("yourUsername");
//find the password input box, here it has id=passwd
$windowObj->mouseEventOnElement("[id=passwd]", 'leftclick');
//type your password
$windowObj->sendKeyPresses("yourPassword");
//click on the login button, here it has id=login
$windowObj->mouseEventOnElement("[id=login]", 'leftclick');
//i assume this is when you encounter the CAPTCHA image
//find the CAPTCHA image element, here it has id=captchaImage
$element = $windowObj->getElement("[id=captchaImage]");
$loc = $element['location'];
//tell the screenshot to only get the CAPTCHA image
$windowObj->setRasterSize($loc['top'], $loc['left'], ($loc['right'] - $loc['left']), ($loc['bottom'] - $loc['top']));
$imageData = $windowObj->screenshot("png");
$sessionUID = uniqid();
$saveWindowObj = serialize($windowObj);
//save the window object so we can pick it up again
file_put_contents("/tmp/" . $sessionUID, $saveWindowObj);
}
//now render the CAPTCHA image to the user as part of a form they can resubmit and make sure to keep the $sessionUId as a hidden variable in the form on the page
End of the "Setup" session, php shuts down here.
Start of "Working" session:
We assume the user submits the form and it is a post containing the $sessionUID and the text string for CAPTCHA.
if (isset($_POST['sessionUID']) === true && isset($_POST['captchaTxt']) === true) {
$savedWindow = file_get_contents("/tmp/" . $sessionUID);
//delete the saved object
unlink("/tmp/" . $sessionUID);
//bring back the object to life
$windowObj = unserialize($savedWindow);
//make sure the browser is now shutdown on exit
$windowObj->getBrowser()->setKeepalive(false);
//find the CAPTCHA input box, here it has id=captchaInput
$windowObj->mouseEventOnElement("[id=captchaInput]", 'leftclick');
//type the CAPTCHA string
$windowObj->sendKeyPresses($_POST['captchaTxt']);
//click on the button to accept CAPTCHA, here it has id=captchaOK
$windowObj->mouseEventOnElement("[id=captchaOK]", 'leftclick');
//now use the clickElement() etc functions on $windowObj to do what you need to do.
}
End of the "Working" session, php shuts down here.
I have the following method that creates a cookie in the page's response. Immediately after I create the cookie, I checked to see what the value was and it was blank. Is this normal behavior? How can I check the response for a cookie after it has been added?
When I monitor the HTTP response, the cookie is there. When I use the Firefox Web Developer Toolbar to view the cookie, it is there. However, it doesnt appear to be present in the Response.Cookies collection. How do I access it?
Dim c As New HttpCookie("prpg")
c.Expires = DateTime.Now.AddYears(10)
c.Value = value
_page.Response.Cookies.Add(c)
_page.Response.Cookies("prpg").Value 'String.Empty
You can only write to Response not read from it. So I suppose it is not possible to read cookies too.