码迷,mamicode.com
首页 > 其他好文 > 详细

基于 cookie 的 TempData 提供程序

时间:2015-08-11 12:04:44      阅读:322      评论:0      收藏:0      [点我收藏+]

标签:

原文链接http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/
TempData是一个不错的功能,在 MVC 中,如果我没有记错,启发Flash 模块从滑轨上的红宝石。它基本上是保持某种状态跨重定向方法。

在 Rails 的默认实现使用 cookie 来存储数据,这使得这相当轻量的机制,将数据从一个请求传递到下一个。不幸的是 TempData 在 MVC 的默认实现使用会话状态作为后备存储使得它不太理想。这就是为什么我想要展示如何构建一种实现,使用 cookie,所以这里是CookieTempDataProvider.

在实现这一点,重要的是承认 TempData 中存储的数据正在向客户端发出作为一个 cookie,这意味着它是开放的查看和修改由最终用户。因此,我想要添加保护被修改和查看 (修改是较为重要的两个)。执行使用相同的保护设施作为 ASP.NET 机关键机制进行加密和签名表单身份验证 cookie 和视图状态。由于 cookie 的大小有限,我们也正在做压缩。

该代码是在GitHub上可用。我也包装这起到 NuGet 程序包 (BrockAllen.CookieTempData) 所以一切必要是引用该程序集通过 NuGet,现在,所有的控制器将使用基于 Cookie 的 TempData 提供程序。如果你感兴趣模式的详细信息,请继续阅读...

该代码是不言自明的:

public class CookieTempDataProvider : ITempDataProvider
{
    const string CookieName = "TempData";

    public void SaveTempData(
        ControllerContext controllerContext,
        IDictionary<string, object> values)
    {
        // convert the temp data dictionary into json
        string value = Serialize(values);
        // compress the json (it really helps)
        var bytes = Compress(value);
        // sign and encrypt the data via the asp.net machine key
        value = Protect(bytes);
        // issue the cookie
        IssueCookie(controllerContext, value);
    }

    public IDictionary<string, object> LoadTempData(
        ControllerContext controllerContext)
    {
        // get the cookie
        var value = GetCookieValue(controllerContext);
        // verify and decrypt the value via the asp.net machine key
        var bytes = Unprotect(value);
        // decompress to json
        value = Decompress(bytes);
        // convert the json back to a dictionary
        return Deserialize(value);
    }
    string GetCookieValue(ControllerContext controllerContext)
    {
        HttpCookie c = controllerContext.HttpContext.Request.Cookies[CookieName];
        if (c != null)
        {
            return c.Value;
        }
        return null;
    }

    void IssueCookie(ControllerContext controllerContext, string value)
    {
        HttpCookie c = new HttpCookie(CookieName, value)
        {
            // don‘t allow javascript access to the cookie
            HttpOnly = true,
            // set the path so other apps on the same server don‘t see the cookie
            Path = controllerContext.HttpContext.Request.ApplicationPath,
            // ideally we‘re always going over SSL, but be flexible for non-SSL apps
            Secure = controllerContext.HttpContext.Request.IsSecureConnection
        };

        if (value == null)
        {
            // if we have no data then issue an expired cookie to clear the cookie
            c.Expires = DateTime.Now.AddMonths(-1);
        }

        if (value != null || controllerContext.HttpContext.Request.Cookies[CookieName] != null)
        {
            // if we have data, then issue the cookie
            // also, if the request has a cookie then we need to issue the cookie
            // which might act as a means to clear the cookie 
            controllerContext.HttpContext.Response.Cookies.Add(c);
        }
    }

    string Protect(byte[] data)
    {
        if (data == null || data.Length == 0) return null;

        return MachineKey.Encode(data, MachineKeyProtection.All);
    }

    byte[] Unprotect(string value)
    {
        if (String.IsNullOrWhiteSpace(value)) return null;

        return MachineKey.Decode(value, MachineKeyProtection.All);
    }

    byte[] Compress(string value)
    {
        if (value == null) return null;

        var data = Encoding.UTF8.GetBytes(value);
        using (var input = new MemoryStream(data))
        {
            using (var output = new MemoryStream())
            {
                using (Stream cs = new DeflateStream(output, CompressionMode.Compress))
                {
                    input.CopyTo(cs);
                }

                return output.ToArray();
            }
        }
    }

    string Decompress(byte[] data)
    {
        if (data == null || data.Length == 0) return null;

        using (var input = new MemoryStream(data))
        {
            using (var output = new MemoryStream())
            {
                using (Stream cs = new DeflateStream(input, CompressionMode.Decompress))
                {
                    cs.CopyTo(output);
                }

                var result = output.ToArray();
                return Encoding.UTF8.GetString(result);
            }
        }
    }

    string Serialize(IDictionary<string, object> data)
    {
        if (data == null || data.Keys.Count == 0) return null;

        JavaScriptSerializer ser = new JavaScriptSerializer();
        return ser.Serialize(data);
    }

    IDictionary<string, object> Deserialize(string data)
    {
        if (String.IsNullOrWhiteSpace(data)) return null;

        JavaScriptSerializer ser = new JavaScriptSerializer();
        return ser.Deserialize<IDictionary<string, object>>(data);
    }
}

通常,使用自定义的TempDataProvider你必须重写CreateTempDataProvider控制器基类这样:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    protected override ITempDataProvider CreateTempDataProvider()
    {
        return new CookieTempDataProvider();
    }
}

益 — — 这意味着你必须重写此在每个控制器或必须提前计划和创建整个应用程序常见的控制器基类。幸运的是还有另一种方式 — — TempDataProvider 是控制器基类上不可忽视。这意味着控制器创建后,您可以指定它,这很容易做到在一个自定义控制器工厂进行 NuGet 程序包中:

class CookieTempDataControllerFactory : IControllerFactory
{
    IControllerFactory _inner;
    public CookieTempDataControllerFactory(IControllerFactory inner)
    {
        _inner = inner;
    }

    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        // pass-thru to the normal factory
        var controllerInterface = _inner.CreateController(requestContext, controllerName);
        var controller = controllerInterface as Controller;
        if (controller != null)
        {
            // if we get a MVC controller then add the cookie-based tempdata provider
            controller.TempDataProvider = new CookieTempDataProvider();
        }
        return controller;
    }

    public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
    {
        return _inner.GetControllerSessionBehavior(requestContext, controllerName);
    }

    public void ReleaseController(IController controller)
    {
        _inner.ReleaseController(controller);
    }
}

最后一件事是配置自定义控制器厂,并从单独的程序集这样做是通过PreApplicationStartMethod允许代码运行在Applicaton_Start之前的魔力:

[assembly: PreApplicationStartMethod(typeof(BrockAllen.CookieTempData.AppStart), "Start")]

namespace BrockAllen.CookieTempData
{
    public class AppStart
    {
        public static void Start()
        {
            var currentFactory = ControllerBuilder.Current.GetControllerFactory();
            ControllerBuilder.Current.SetControllerFactory(new CookieTempDataControllerFactory(currentFactory));
        }
    }
}
原文链接http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/
代码路径

基于 cookie 的 TempData 提供程序

标签:

原文地址:http://www.cnblogs.com/shatanku/p/4720342.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!