Windows Day: El futuro de la plataforma de desarrollo web hoy
[English version below]
En el día de hoy Microsoft lanzó Windows Day, un evento online en donde todos los desarrolladores y profesionales IT interesados podrán asistir a charlas y demostraciones de las tecnologías que hoy en día se estan utilizando y de las que vendrán.
Brian y yo tuvimos la oportunidad de participar en una charla llamada "El futuro de la plataforma de desarrollo web hoy" en donde hablamos sobre las innovaciones de ASP.NET 4.0 entre otras cosas:
-
ASP.NET MVC
-
Dynamic Data
-
Client Templates
-
IE 8 & Developer Tools
-
jQuery
Pueden descargar aquí las diapositivas y el código fuente de la demo de Dynamic Data, y del blog de Brian el de Client Templates.
Espero que les guste, críticas y comentarios son siempre bienvenidos!
[English version]
Today Microsoft launched Windows Day, an online event where everyone involved with IT could take part in lectures and demos about the technologies developers are using right now, and some others which are coming.
Brian and me had the opportunity to collaborate with a topic called "The future of web development, today" where we talk about the new features in ASP.NET 4.0 and some other stuff:
- ASP.NET MVC
- Dynamic Data
- Client Templates
- IE 8 & Developer Tools
- jQuery
You can download here the slides and the Dynamic Data demo source code, and from Brian's blog the Client Templates source code.
I hope you like it, critics and comments are always welcome!
Internationalization in ASP.NET MVC
Anyone who is starting almost any kind of software nowadays should be thinking on internationalization, even more if it is web-based. The effort required to prepare a system for localization (the difference between internationalization and localization is subtle, you can find it on Wikipedia) from the very beginning is minimum and the payoff is huge.
In this post I will first describe in the large how we can approach this problem with ASP.NET MVC, and then I will provide some easy steps to accomplish the task.
General idea of i18n
We will start by setting all the strings that our system uses in a key-value pair file. We will have one of these files for each language we choose to support, for instance the values for the key “colors” would be “colors”, “colours” and “colores” in the files Resources.en-US.resx, Resources.en-UK.resx and Resources.es.resx respectively. After that we only have to know which file to pick, in order to do that we read the preferred language sent by the browser on every Http request. Finally we should give the user the option to change the language, imagine that an English speaker would not be very happy to check his e-mail with the browser preferred language during his vacations in China!
1. Create the resources files
The first thing we are going to do is to create the resources file/s. Right-click on the App_GlobalResources folder, add a new resource file and name it Resources.resx: this would be the default language. By adding this item, another file called Resource.designer.cs is added to the project, it provides direct access to the key-value pairs we define on the resource file/s. In this post we will show how to support English (default) and Spanish, so we add another resource file called Resources.es.resx. Finally we add some string to those files, for instance: “Welcome” associated with “Welcome to MySite!” and “Bienvenido/a a MiSitio!”.
When we access these string within the code the Framework first try to do an exact match with the language. For instance if the language is set to es-AR (Spanish from Argentina) the framework will look first for the more specific resource file (Resources.es-AR.resx in this case), if that file does not exist the one corresponding to the parent language would be chosen (Resources.es.resx) and if that is not found either, then the default resource file is used.
2. Use the browser preferred language
In ASP.NET MVC is very easy to set the current culture to the browser’s preference. We just need to add a single line to the Web.config file: after the <system.web> tag we add:
<system.web> <globalization culture="auto" uiCulture="auto"/>
Now we are ready to test what we have done so far. We just need to change the access modifier of the default resource file from internal to public. Then every string defined on these files could be accessed by just writing:
App_GlobalResources.Resources.Welcome;
3. Set the user preferred language and store it in a cookie
We have two ways to do this: server-side or client-side. In my personal opinion, as we only need to create and store a cookie and reload the site, is not convenient to go to the server. On the other hand, doing this client-side could be a bit more difficult. Luckily, we have jQuery!
First we need to have some links or cute images associated with onclick events:
<a href="#" onclick="changeLang('es-AR')">es-AR</a> <a href="#" onclick="changeLang('en-US')">en-US</a>
On the <head> section we need to import jQuery (line 1) and the cookie plugin (line 2), which can be found here and a nice demo here.
1: <script src="/Scripts/jquery-1.2.6.js" type="text/javascript"></script>
2: <script src="/Scripts/jquery.cookie.js" type="text/javascript"></script>
3: <script type="text/javascript">
4: function changeLang(lang) {
5: $.cookie('lang', lang);
6: window.location.reload();
7: return false;
8: }
9: </script>
In line 5 the jQuery Cookie Plugin is used to store the chosen language in a variable named ‘lang’, and the statement in line 6 tell the browser to reload the site. What for? We will see in the next (and final) step.
4. Eat the cookie (before everybody else!)
As we all know, every cookie which is associated with the current domain is sent with every Http request. It would be great if we could write some code to “catch” each request, see if there is a cookie with the preferred language, and set it.
ASP.NET works with Http Modules which, in short, are pieces of code that can play with the Http request before it reaches to the handlers. Mansoor Ahmed Siddiqui wrote a great post about this, which I strongly recommend.
First we register the module in the Web.config file, so the Framework knows it have to be executed with every request:
<httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <!-- this one is our module =) --> <add name="CookieLocalizationModule" type="MyProject.CookieLocalizationModule, MyProject"/> <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> </httpModules>
Finally the code, which is pretty what you can image: getting the cookie and setting the language, with some more lines the modules require:
public class CookieLocalizationModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication context) { context.BeginRequest += new EventHandler(context_BeginRequest); } void context_BeginRequest(object sender, EventArgs e) { // eat the cookie (if any) and set the culture if (HttpContext.Current.Request.Cookies["lang"] != null) { HttpCookie cookie = HttpContext.Current.Request.Cookies["lang"]; string lang = cookie.Value; var culture = new System.Globalization.CultureInfo(lang); Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; } } }
And that is all?
Almost. There is one more thing we need to do: internationalize images. Although it is quite an easy task and there are many ways of doing it, this post is long enough so I will write about it later.
This should be enough for lots of applications and many languages. However there exists some issues related with Arabic languages (which are written from right to left) and some Asian ones (which use symbols) that are not at all cover in this post.
Thanks a lot Brian for all the help!
I hope this have been useful. Have a nice day!
Adding e-mail verification on ASP.NET MVC using Membership Framework.
Since last week I have been learning and working with ASP.NET MVC Beta. I found a lot of interesting and easy-to-use things which convinced me that it is a good choice for web development. The Membership Framework is one of those.
Unfortunately a more-than-common feature like user e-mail verification is not built-in so I had to do it myself. Here are the three easy steps you should follow in order to add this functionality to your site:
Store the user as unapproved
We create the new user as usual, except for the value isApproved which we should set to false. This indicates that the user is on the database but is inactive and cannot log in (until we verify his or her e-mail).
1: // Attempt to register the user
2: MembershipCreateStatus createStatus;
3: MembershipUser newUser = Provider.CreateUser(username, password, email, pwQuestion, pwAnswer, false, null, out createStatus);
4:
5: if (newUser != null)
6: {
7: // The user was created successfully, we send the verification e-mail.
8: SendVerificationMail(newUser);
9: return Content("Your account have been created. Please check your e-mail to activate it.");
10: }
11: else
12: {
13: // There were errors, show them to the user.
14: ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
15: }
Prepare the verification e-mail
The most important thing in this part is to provide the user with a link to verify his or her account. The link must be difficult for anyone to create. In this example we will use the global unique identifier or guid which is a good trade-off between security and code simplicity. For more information about GUIDs you can check MSDN. We will see a way to enhance security later.
1: private void SendVerificationMail(MembershipUser user)
2: {
3: MailMessage email = new MailMessage(
4: "info@yourSite.com",
5: user.Email
6: );
7:
8: Guid guid = (Guid)user.ProviderUserKey;
9: email.Subject = "Welcome!";
10: email.Body = "Activation link: yourSite/controller/action/" + guid.ToString();
11:
12: SmtpClient smtp = new SmtpClient("your_smtp");
13: smtp.Credentials = CredentialCache.DefaultNetworkCredentials;
14:
15: try {
16: smtp.Send(email);
17: }
18: catch (Exception exc){
19: // Handle exception
20: }
21: }
Note that the link points to some controller and action we have not defined yet. We can use an existing controller (maybe the AccounController) or a new one. We might also use a different URL routing, though I decided to keep the default for simplicity.
Verifying the link
One more step and we are ready to go live! Now we should receive the request and verify that the GUID is the right one. If everything is OK we approve the user.
1: public ActionResult Verify(string id)
2: {
3: Guid guid = new Guid(id);
4: MembershipUser user = Membership.GetUser(guid);
5: if (user != null && user.IsApproved == false)
6: {
7: user.IsApproved = true;
8: Membership.UpdateUser(user);
9: FormsAuth.SetAuthCookie(user.UserName, false);
10: }
11: else {
12: // Wrong GUID or user was already approved
13: }
14:
15: return View();
16: }
And that’s all! Quite an interesting feature in very few and easy steps.
Enhancing security
Though using a Global Unique Identifier provides a quite secure environment sometimes Security is such an important matter that we should think in adding some more complications to those good hackers out there
.
A good option would be adding another parameter to the action, like an MD5 hash applied to the email or username, or a combination. Useful help is, again, on MSDN.
Why I started a blog?
Many times I have faced problems during my day-to-day tasks. Always, the first attemp to solve them is surfing the web. Sometimes I find help on official sites, sometimes I end up asking a friend, but many times the best help is on some blog round there. Then I thought I could add my little piece of contribution to this little big word of IT and that is what this blog would (try) to be.
I hope that my experiences (both good and bad ones!) would be found useful for some of you. And if not, at least have fun!
SERch.-