mvc3 simple custom output caching - asp.net-mvc-3

I'm trying to follow some tutorial where on one place I should override GetVaryByCustomString ....
Code is following
public override string GetVaryByCustomString(HttpContext context, string arg)
{
// It seems this executes multiple times and early, so we need to extract language again from cookie.
if (arg == "culture") // culture name (e.g. "en-US") is what should vary caching
{
string cultureName = null;
// Attempt to read the culture cookie from Request
HttpCookie cultureCookie = Request.Cookies["_culture"];
if (cultureCookie != null)
cultureName = cultureCookie.Value;
else
cultureName = Request.UserLanguages[0]; // obtain it from HTTP header AcceptLanguages
// Validate culture name
cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe
return cultureName.ToLower();// use culture name as cache key, "es", "en-us", "es-cl", etc.
}
return base.GetVaryByCustomString(context, arg);
}
Question is: Where I should put these code, in which class?

You can override it in Global.asax

Related

TempData not kept between postback

I need some advice on how to proceed with the mvc app I'm building. On my page I type out who is logged in to the page. This I first did by creating a base class where I created a user class which contained the users name and a image representing the user. Then I passed this class on to my views. But I also need to pass other models to my views depending on what view I'm in. Sure I could build a class that contain all different models I need to use on each page but there should be a easy way to pass name and image values across the pages and be persistant? I tried TempData together with TempData.Keep() but that was not persistant. How can I pass theses values between pages?
public ActionResult Validate(AccountModels.LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var mu = _repo.GetUser(Membership.GetUser().ProviderUserKey.ToString());
TempData["Name"] = mu.Name;
TempData["Image"] = mu.Image;
TempData.Keep();
FormsAuthentication.RedirectFromLoginPage(model.UserName, model.RememberMe);
}
}
return View("Index");
}
As #Jyoti said, you could use of Keep() method.
To make it easy to work with TempData, I wrote these methods in my BaseController, and I use it in every controller when I need to transfer data between actions or between view and controller.
protected TReturnType GetTempDataValue<TReturnType>(PsmConstants.TempDataKey sessionName, bool peekData =false )
{
object value = peekData ? TempData.Peek(sessionName.ToString()) : TempData[sessionName.ToString()];
return (TReturnType) value;
}
protected void RemoveTempData(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()) && TempData[sessionName.ToString()] == null) return;
TempData[sessionName.ToString()] = null;
}
protected void SetTempDataValue(PsmConstants.TempDataKey sessionName, object value)
{
if(TempData.ContainsKey(sessionName.ToString()))
TempData[sessionName.ToString()]=null;
TempData[sessionName.ToString()] = value;
}
protected void KeepTempDataValue(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()))
TempData.Keep(sessionName.ToString());
}
And this is the Keys enumeration :
public enum TempDataKey
{
PageError = 1,
PageInfo = 2
}
And this is, the usage of these methods(Set value and Get value from TempData):
SetTempDataValue(PsmConstants.TempDataKey.PageError , 'your error message' );
var originalValues = GetTempDataValue<MyModel>(PsmConstants.TempDataKey.Info, true);
Use session instead of Temp if it is not working.but i think it should work.
TempData["Name"] = mu.Name;TempData["Image"] = mu.Image;TempData.Keep();
How you are passing this into other models,Please share the source code so that it will easy to identify.

Sending new parameters in MvxViewModelRequest from a IMvxNavigationFacade when deeplinking

I am using deeplinking in my app and Im looking to preset some parameters when navigating to the viewmodel using a IMvxNavigationFacade. The deep link url is like this:
myappname://deeplink/toviewwithdata/?navigatetoview=viewtype1&id=78910
So the deep linking is working and im getting to the navigation facade using the assembly attribute
[assembly: MvxNavigation(typeof(RoutingFacade), #"myappname://deeplink/toviewwithdata/\?navigatetoview=(?<viewtype>viewtype1)&id=(?<id>\d{5})")]
I tried to add other parameters to the MvxViewModelRequest using a MvxBundle but dont think im doing it right. here is my navigation facade:
public class RoutingFacade : IMvxNavigationFacade
{
public Task<MvxViewModelRequest> BuildViewModelRequest(string url, IDictionary<string, string> currentParameters)
{
var viewModelType = typeof(FirstViewModel);
var parameters = new MvxBundle();
try
{
// TODO: Update this to handle different view types and add error handling
if (currentParameters != null)
{
Debug.WriteLine($"RoutingFacade - {currentParameters["viewtype"]}, {currentParameters["id"]}");
switch (currentParameters["viewtype"])
{
case "viewtype1":
viewModelType = typeof(FirstViewModel);
parameters.Data.Add("test", "somevalue");
break;
default:
case "viewtype2":
viewModelType = typeof(FirstViewModel);
break;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"RoutingFacade - Exception: {ex.Message}");
//TODO viewModelType = typeof(ErrorViewModel);
}
return Task.FromResult(new MvxViewModelRequest(viewModelType, parameters, null));
}
then my viewmodel Init method
public void Init(string id, string viewtype, string test)
{
// Do stuff with parameters
}
but the test parameter is null? How do you pass parameters into a MvxViewModelRequest?
Update:
Don’t know if its possible from looking at the source here https://github.com/MvvmCross/MvvmCross/blob/f4b2a7241054ac288a391c4c7b7a7342852e1e19/MvvmCross/Core/Core/Navigation/MvxNavigationService.cs#L122 as the request parameters get set from the regex of the deeplink url and the return from BuildViewModelRequest, facadeRequest.parameterValues get ignored.
Added this functionality in this pull request

MVC 3 Request.Cookies value switches dynamically without purpose

I have a problem with redirection in my BaseController (MVC 3) I have two langage versions of my site polish and english (set by $.cookie plugin). If, is set the english language and user wants to go to the polish URL - I want to make a proper redirection. But, anytime I make the redirection within the OnActionExecuting method, in the ExecuteCore method, I see that the language switches for some reason - see the (*) line.
What's more, in that scenario I get the infinite redirections between OnActionExecuting and ExecuteCore methods. Why ?
public class BaseController : Controller
{
protected override void ExecuteCore()
{
if (Request.Cookies["language"] != null)
{
if (Request.Cookies["language"].Value != "pl-PL" && Request.Cookies["language"].Value != "en-US")
Request.Cookies["language"].Value = "en-US";
}
else
Request.Cookies.Add(new HttpCookie("language", "en-US"));
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(Request.Cookies["language"].Value);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
base.ExecuteCore();
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
switch (filterContext.ActionDescriptor.ActionName)
{
case "Onas":
if (Request.Cookies["language"].Value == "en-US")
{
filterContext.Result = new RedirectResult("/About", true);
return;
}
break;
case "About":
if (Request.Cookies["language"].Value == "pl-PL")
{
(*) filterContext.Result = new RedirectResult("/Onas", true); // Request.Cookies["language"].Value will be en-US inside the ExecuteCore method - why ?
return;
}
break;
}
base.OnActionExecuting(filterContext);
}
}
Check the cookies Path property. You could get infinite redirections by having en-US on /Onas url and pl-PL on /About url.
Make sure to set global cookies with jQuery plugin with { path: '/' }

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

Silverlight localized custom validation using DataAnnotations with RIA Services

I've implemented localized validation, client-side, using the DataAnnotations attributes successfully. Now, I want to implement custom validation running server-side using the CustomValidationAttribute but my problem is that I can't find a way to get the client-side culture while executing the validation.
Here's the setup for the custom validation method:
public static ValidationResult ValidateField( string fieldValue, ValidationContext validationContext )
{
#if !SILVERLIGHT
// Get the message from the ValidationResources resx.
return new ValidationResult( ValidationResources.Message, new string[]{ "Field" } );
#else
return ValidationResult.Success;
#endif
}
This code returns the message but from the culture that the server is currently set.
I also tried to set the attribute on the property this way with same result:
[CustomValidation( typeof( CustomValidation ), "ValidateField", ErrorMessageResourceName = "Message", ErrorMessageResourceType = typeof( ValidationResources ) )]
I also tried to expose a method on my DomainService to change the Culture on the ValidationResources resx but this seems to be changing the culture not only or the current connection but for all the connections.
Since the validation is ran by Ria Services and not something I am calling directly, how can I tell the validation method to use a specific culture?
I came across this thread and I was able to fix my issue and have the culture name pass to every request made by the DomainContext (client) to the server.
First, we need to create a custom IClientMessageInspector that will be responsible to set a parameter for the CurrentUICulture for every requests.
public class AppendLanguageMessageInspector : IClientMessageInspector
{
#region IClientMessageInspector Members
public void AfterReceiveReply( ref Message reply, object correlationState )
{
// Nothing to do
}
public object BeforeSendRequest( ref Message request, IClientChannel channel )
{
var property = request.Properties[ HttpRequestMessageProperty.Name ] as HttpRequestMessageProperty;
if( property != null )
{
property.Headers[ "CultureName" ] = Thread.CurrentThread.CurrentUICulture.Name;
}
return null;
}
#endregion // IClientMessageInspector Members
}
Next, we need to create a custom WebHttpBehavior that will inject our custom IClientMessageInspector.
public class AppendLanguageHttpBehavior : WebHttpBehavior
{
public override void ApplyClientBehavior( ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime )
{
clientRuntime.MessageInspectors.Add( _inspector );
}
private readonly AppendLanguageMessageInspector _inspector = new AppendLanguageMessageInspector();
}
Finally, we extend the client DomainContext.OnCreate method to add our custom WebHttpBehavior. NOTE: The namespace of the extended DomainContext class must be the same as the generated one...
public partial class DomainService1
{
partial void OnCreated()
{
var domainClient = this.DomainClient as WebDomainClient<IDomainService1Contract>;
if( domainClient != null )
{
domainClient.ChannelFactory.Endpoint.Behaviors.Add( DomainService1.AppendLanguageHttpBehavior );
}
}
private static readonly AppendLanguageHttpBehavior AppendLanguageHttpBehavior = new AppendLanguageHttpBehavior();
}
Now, on the server-side, when we want to get the language code we can simply access it like this:
var cultureName = System.Web.HttpContext.Current.Request.Headers[ "CultureName" ];
To enjoy even more of the DataAnnotation magic, we can even change the CurrentUICulture in the Initialize of the DomainService like this:
public override void Initialize( DomainServiceContext context )
{
var cultureName = System.Web.HttpContext.Current.Request.Headers[ "UICultureName" ];
Thread.CurrentThread.CurrentUICulture = new CultureInfo( cultureName );
base.Initialize( context );
}

Resources