Sunday, January 31, 2016

How to handle ASP.NET MVC JSON Result Date Format Issue

ASP.Net MVC controller will return JsonResult for action so that ajax can handle those Json data. But Json data has issues for type DateTime, especially Microsoft SQL Server DB. Here is some result if you just display those data in string format: "/Date(-501361200000)/". This is one very popular topic for ASP.Net MVC Json data processing. You can process those Json DateTime at client side using some Javascript tricks. But the best solution is on the Server side, you can use Json.Net from newtonsoft to do the magic.

This is one typical action method in controller:
        public JsonResult GetStudent(string name)
        {
            try
            {
                if (name == null)
                {
                    return Json(null, JsonRequestBehavior.AllowGet);
                }
                else
                {
                    using (StudentService _service = new StudentService())
                    {
                        Student = _service.GetStudent(name);
                        return Json(model, JsonRequestBehavior.AllowGet);
                    }
                }
            }
            catch (Exception ex)
            {
                return Json(ex.Message, JsonRequestBehavior.AllowGet);
            }
        }
You don't need to change above code. What you should do is:
First,  add a new class CustomJsonResult which inherits from JsonResult.
using System;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public class CustomJsonResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        if (!String.IsNullOrEmpty(ContentType))
        {
            response.ContentType = ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data != null)
        {
            // Using Json.NET serializer
            var isoConvert = new IsoDateTimeConverter();
            isoConvert.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
            response.Write(JsonConvert.SerializeObject(Data, isoConvert));
        }
    }
}
Second, in your Base Controller, override Json method to return CustomJsonResult.
        protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
        {
            return new CustomJsonResult
            {
                Data = data,
                ContentType = contentType,
                ContentEncoding = contentEncoding,
                JsonRequestBehavior = behavior
            };
        }
Then all JsonResult call in your controller will return CustomJsonResult I created above, which use Newtonsoft.Json.Converters.IsoDateTimeConverter as serializer instead, and you will see the readable DateTime instead of annoying and wild date format in JsonResult.

Source:
http://stackoverflow.com/questions/726334/asp-net-mvc-jsonresult-date-format

Friday, January 15, 2016

Applying c# Extensions for Claims-based Authentication

In My AccountController, I have NewLogin Action:
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult NewLogin(LocalAccountLoginViewModel model, string returnUrl)
        {
            using (UserService _service = new UserService())
            {
                CFG_Users user = _service.GetUserById(model.UserId);
                if (user == null)
                {
                    DisplayErrorPage("Error", Resources.Account.LoginResources.UserNotExist);
                    return View(model);
                }
                if (!user.Enabled)
                {
                    DisplayErrorPage("Error", Resources.Account.LoginResources.UserNotActive);
                    return View(model);
                }

                if (!_service.ValidateUser(model.UserId, model.Password))
                {
                    DisplayErrorPage("Error", Resources.Account.LoginResources.InvalidUserNamePassword);
                    return View(model);
                }

                var claims = new List<Claim>();

                // create *required* claims
                claims.Add(new Claim(ClaimTypes.NameIdentifier, user.IDUser));
                claims.Add(new Claim(ClaimTypes.Name, _service.GetUserName(user)));
                claims.Add(new Claim("IsAdmin", _service.IsUserAdmin(user).ToString()));
                claims.Add(new Claim("Culture", user.IDLanguage.Trim()));

                IdentitySignin(claims, model.UserId, model.RememberMe);

                if (!string.IsNullOrEmpty(returnUrl))
                    return Redirect(returnUrl);

                return RedirectToAction("Index", "Dashboard", null);
            }
        }
The NewLogin action  applied following helper method to finish the sign in process.
        /// <summary>
        /// Helper method that adds the Identity cookie to the request output
        /// headers. 
        /// </summary>
        /// <param name="claims"></param>
        /// <param name="providerKey"></param>
        /// <param name="isPersistent"></param>
        public void IdentitySignin(List<Claim> claims, string providerKey = null, bool isPersistent = false)
        {
            var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);

            // add to user here!
            AuthenticationManager.SignIn(new AuthenticationProperties()
            {
                AllowRefresh = true,
                IsPersistent = isPersistent,
                ExpiresUtc = DateTime.UtcNow.AddDays(1)
            }, identity);

            var userCulture = identity.GetCulture();
            // set lang to the user's language 
            if (RouteData.Values["lang"] != userCulture)
            {
                RouteData.Values["lang"] = userCulture;
                SetThreadCulture(userCulture);
            }
        }

        public void IdentitySignout()
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie,
                DefaultAuthenticationTypes.ExternalCookie);
        }

        private IAuthenticationManager AuthenticationManager
        {
            get { return HttpContext.GetOwinContext().Authentication; }
        }
Note I applied C# Extensions so I can easily use GetCulture for my identity. Here is the Claim Identity Extension code:
using System.Collections.Generic;
using System.Security.Claims;
using System.Linq;
namespace MyApp.Extensions
{
    public static class ClaimsIdentityExtensions
    {
        public static bool IsAdmin(this ClaimsIdentity identity)
        {
            var claims = identity.Claims;
            return bool.Parse(GetClaim(claims, "IsAdmin")); 
        }

        public static string GetCulture(this ClaimsIdentity identity)
        {
            var claims = identity.Claims;
            return GetClaim(claims, "Culture"); //todo: replace Culture with a Const
        }

        public static string GetName(this ClaimsIdentity identity)
        {
            var claims = identity.Claims;
            return GetClaim(claims, ClaimTypes.Name); ;
        }

        public static string GetUserId(this ClaimsIdentity identity)
        {
            var claims = identity.Claims;
            return GetClaim(claims, ClaimTypes.NameIdentifier);
        }

        private static string GetClaim(IEnumerable<Claim> claims, string key)
        {
            var claim = claims.ToList().FirstOrDefault(c => c.Type == key);
            if (claim == null)
                return null;
            return claim.Value;
        }
    }
}

Reference:
http://leastprivilege.com/2012/10/08/custom-claims-principals-in-net-4-5/

Friday, January 8, 2016

How to share Localization Resource in ASP.Net 5 MVC with Javascript

ASP.Net MVC projects can use Resource files to do localization. Its very easy to use those resource files in razor view files. For each resource, there are multiple resource files referring to different languages. ASP.Net treats those resource files as one resource class. The application will use specific language resource during the run time while one language is selected.

Regarding the Javascript, if we want to apply those resource files, we need to send them to Javascript one by one in razor file. To avoid those tedious process and to improve the independence of Javascript, we can create resource section in razor file using HTML 5 section tag. And let Javascript to read those resource by themselves. Here is the code in view:
<section class="myResouceLabels"
    data-Message1 = "@MyResources.MyMessage1"
    data-Message2 = "@MyResources.MyMessage2">
</section>
In your Javascript:
    // Return a message contained in the container
    function GetResourceMessage(msgId) {
        var container = $(".myResouceLabels");
        if (container.length == 0) return;
        return container.attr(msgId);
    }
    ....
    alert(GetResourceMessage(data-Message1);
    alert(GetResourceMessage(data-Message1);
Note section tag contained all attributes with data as leading string, which is different than other tags. Be careful not to make mistake!!!

Definitely, you need css decorating to let the section don't be displayed.
myResouceLabels { display: none }