标签:
原文链接http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/
在 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/
代码路径
标签:
原文地址:http://www.cnblogs.com/shatanku/p/4720342.html