不用写代码可以做网站的软件深圳龙华有什么好玩的地方推荐
news/
2025/9/23 17:24:31/
文章来源:
不用写代码可以做网站的软件,深圳龙华有什么好玩的地方推荐,南京做企业网站的公司,设计最简单的企业网站原文来自互联网#xff0c;由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权#xff0c;请联系小编#xff0c;小编将在24小时内删除。限于译者的能力有限#xff0c;个别语句翻译略显生硬#xff0c;还请见谅。作者简介#xff1a;Jon#xff08;Jonathan#x… 原文来自互联网由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权请联系小编小编将在24小时内删除。限于译者的能力有限个别语句翻译略显生硬还请见谅。作者简介JonJonathanSeeley一位资深.NET开发者主要从事Asp.NET/Asp.NET CORE/WPF等技术栈的开发他的博客地址为https://www.seeleycoder.com/。原文链接[1]对于那些习惯于在传统ASP.NET中使用Cookie的人来说改用ASP.NET Core可能会让我们抓狂。在旧系统中我们能够直接从请求和响应对象中添加和删除cookie无论好坏。这可能导致我们在请求期间多次写入和覆盖相同的cookie因为不同部分的代码会影响它。DotNetCore改变了游戏规则这是一件好事相信我。今天我们将学习DotNetCore Web应用程序中的cookie管理技术。这篇文章的所有代码都可以在我的GitHub上找到[2]。了解过去为了论证我想介绍一下传统的ASP.NET MVC中用于加载Cookie的“通用”代码。当然问题在于如果代码中的某处设置了cookie值而我们稍后又在寻找它我们想确保我们始终获得最新的副本而不必一定是请求中包含的内容。下面的代码看起来是否响应中首先匹配。public static System.Web.HttpCookie GetCookie(this System.Web.HttpContextBase context, string keyName)
{System.Web.HttpCookieCollection cookies new System.Web.HttpCookieCollection();System.Web.HttpCookie cookie null;// check for response value first...if (context.Response.Cookies.AllKeys.Any(key string.Equals(key, keyName, StringComparison.OrdinalIgnoreCase)))cookie context.Response.Cookies.Get(keyName);else if (context.Request.Cookies.AllKeys.Any(key string.Equals(key, keyName, StringComparison.OrdinalIgnoreCase)))cookie context.Request.Cookies.Get(keyName);return cookie;
}
因此这就是我们可能访问Cookie进行消费的方式我们在修改过程中会不会无意中把这个流程搞乱了我敢肯定大家也许有很多方式以下这是我可能做过的一个例子public static void SetCookie(this System.Web.HttpContextBase context, string keyName, string value, DateTime? expiry null)
{if (context.Response.HeadersWritten)return;// a null value is equivalent to deletionif (value null){context.Request.Cookies.Remove(keyName);context.Response.Cookies.Add(new System.Web.HttpCookie(keyName, ) { Expires DateTime.Today.AddYears(-1) });return;}System.Web.HttpCookie newCookie new System.Web.HttpCookie(keyName, value);if (expiry.HasValue)newCookie.Expires expiry.Value;context.Response.Cookies.Add(newCookie);
}
在上面的代码中我们试图确保删除cookie也可以防止在未找到同一请求的情况下尝试使用它。如果已经发送了标头我们也将阻止编写cookie因为它将引发异常。该代码“不做”的一件事是防止重复我是故意这样做的。一旦将其写到浏览器中响应中的最后一个将调用因此它仍将按预期“工作”但同样我们还有一个错误。如果您想知道您不想随意context.Response.Cookies.Add但是应该检查它是否已经存在如果存在请调用context.Response.SetCookie。尽管编写一个cookie管理器并确保您所有的cookie代码都能通过它并不困难但对于菜鸟和经验丰富的开发人员来说普遍认为“它可以正常工作”是很常见的。从这个角度来说如果您确实了解了Asp.NET中Cookie的设置方法并习惯了它DotNetCore会让您失望。DotNetCore的差异既然我们已经介绍了一些您可能期望在传统的ASP.NET MVC中执行操作的方式那么强调DotNetCore中的差异非常重要。首先HttpContext.Request.CookiesDotNetCore中的集合不能被修改。希望您在以前的示例中注意到当我们删除传统版本的cookie时我们也删除了请求副本以确保以后不再使用无效的cookie。同样HttpContext.Response.Cookies不允许您删除附加到该项目的项目。当然您可以要求“删除” cookie但这只是修改了到期时间因此浏览器将其删除。一旦请求来了就会调用这个方法。当我用DotNetCore重写大型应用程序并从旧系统“复制”代码时这些差异是我很早就遇到的并导致了对ASP.NET Core中cookie管理的了解。这些差异是一件好事因为它们迫使您对正在做的事情多加思考而不是仅仅假设一切正常。如果使用传统ASP.NET MVC的示例代码来设置Cookie除非小心否则最终可能会在响应中获得cookie的多个副本。如果发生这种情况并且您稍后尝试在同一请求中读取该值则可能实际上并没有获得您希望的结果。这样的操作很糟糕。介绍Cookie Service鉴于我们之间的差异再加上DotNetCore确实尽力让您使用依赖项注入这一事实那么您将如何进行cookie管理我个人认为您所有的cookie管理都应通过服务进行分配然后由中间件负责将最终状态写回到响应中。让我们开始吧public class CachedCookie
{public string Name { get; set; }public string Value { get; set; }public CookieOptions Options { get; set; }public bool IsDeleted { get; set; }
}
public interface ICookieService
{void Delete(string cookieName);T GetT(string cookieName, bool isBase64 false) where T : class;T GetOrSetT(string cookieName, FuncT setFunc, DateTimeOffset? expiry null, bool isBase64 false) where T : class;void SetT(string cookieName, T data, DateTimeOffset? expiry null, bool base64Encode false) where T : class;void WriteToResponse(HttpContext context);
}
public class CookieService : ICookieService
{private readonly HttpContext _httpContext;private Dictionarystring, CachedCookie _pendingCookies null;public CookieService(IHttpContextAccessor httpContextAccessor){_httpContext httpContextAccessor.HttpContext;_pendingCookies new Dictionarystring, CachedCookie();}public void Delete(string cookieName){}public T GetT(string cookieName, bool isBase64 false) where T : class{throw new NotImplementedException();}public T GetOrSetT(string cookieName, FuncT setFunc, DateTimeOffset? expiry null, bool isBase64 false) where T : class{throw new NotImplementedException();}public void SetT(string cookieName, T data, DateTimeOffset? expiry null, bool base64Encode false) where T : class{}public void WriteToResponse(HttpContext context){}
}
在上面的代码块中我添加了一个CachedCookie类 对我们的接口进行了存根CookieService并为我们的服务设置了框架。我们早应了解的一件事是由于某种原因该服务基于泛型。我希望能够将几乎所有的价值写到我的cookie中。在这种情况下我选择将泛型限制在一个类中该类string可以限定但所有基本值类型都将失败。为了使这种魔术起作用我将使用JSON将我的值序列化为字符串。为了弄清楚所有部分如何组合在一起我认为我们将一次迈出这一步。我们的构造函数正在注入IHttpContextAccessor这使我们能够访问HttpContext请求的当前值。这类似于我们曾经使用过的旧ASP.NET HttpContext.Current。但是要使此方法起作用我们需要将其注册因此请跳至Startup.cs您的ConfigureServices方法并将这些行添加到您的方法中services.AddSingletonIHttpContextAccessor, HttpContextAccessor();services.AddScopedICookieService, CookieService();您还会在构造函数中注意到的另一件事是我们正在为的实例设置一个空字典CachedCookie。在中间件将它们转储到响应之前这是我们在请求期间跟踪cookie状态的地方。中间件我们需要照顾的下一件事是创建我们的中间件并将其放入我们的管道中。让我们添加CookieServiceMiddleware.cs并编写下列代码internal class CookieServiceMiddleware
{private readonly RequestDelegate _next;public CookieServiceMiddleware(RequestDelegate next){_next next;}public async Task Invoke(HttpContext context, ICookieService cookieService){// write cookies to response right before it starts writing out from MVC/api responses...context.Response.OnStarting(() {// cookie service should not write out cookies on 500, possibly others as wellif (!context.Response.StatusCode.IsInRange(500, 599)){cookieService.WriteToResponse(context);}return Task.CompletedTask;});await _next(context);}
}
无法在构造函数级别将范围服务注入中间件。您会注意到我在Invoke方法中[3]注入了它这似乎有点像魔术。在DotNetCore底层的某个地方的IServiceProvider组件知道如何进行注入。要注意的另一件事是我检测到响应何时开始然后检查状态码是否不在特定范围内。如果超出该范围那么我们将继续通过服务将Cookie写入响应中。该IsInRange扩展方法是一个我已经添加的话事不宜迟这里是一个基本的IntExtensions.cs添加到项目中我public static class IntExtensions
{public static bool IsInRange(this int checkVal, int value1, int value2){// First check to see if the passed in values are in order. If so, then check to see if checkVal is between themif (value1 value2)return checkVal value1 checkVal value2;// Otherwise invert them and check the checkVal to see if it is between themreturn checkVal value2 checkVal value1;}
}
注册中间件好的 最后我们需要添加到中间件代码中并进行连接。中间件的约定是创建一个静态类和扩展方法来处理中间件的注册。让我们添加CookieServiceMiddlewareExtensionspublic static class CookieServiceMiddlewareExtensions
{public static IApplicationBuilder UseCookieService(this IApplicationBuilder builder){return builder.UseMiddlewareCookieServiceMiddleware();}
}
让我们进入Startup.cs进入我们的Configure方法中并添加app.UseCookieService();到链的某处。这里的窍门是您希望它在您的app.UseMvc呼叫之前以及可能影响您响应的任何其他内容之前显示但不要太高以至于过早地向响应写出cookie。在这种情况下我选择在app.UseCookiePolicy通话后添加它。如果您有很多其他中间件则您自己的工作量可能会有所不同。补充一下。如果我的中间件稍微复杂一点并且有多个服务需要注册那么我可能还创建了一个扩展方法来从我的ConfigureServices方法中调用。如果我正在创建一个用于分发的中间件那么即使只有一个服务我也绝对可以做到。我不想强迫某人必须了解一切才能为DI配置我的中间件他们应该能够简单地要求添加它并继续前进。该扩展方法可能具有这样的签名public static IServiceCollection ConfigureCookieService(this IServiceCollection services, IConfiguration configuration)。这里的IConfiguration是可选的……在某些方面我需要它但是显然在这种情况下我们不需要它。实现太好了我们现在已经注册了我们的服务和中间件但是它什么也没做。让我们继续一次开始实现一种方法。由于我们实际上要尝试做的第一件事是加载cookie以供消费也许我们应该从那里开始。进入CookieService.cs并将以下代码添加到public T Get方法中Get public T GetT(string cookieName, bool isBase64 false)where T : class
{return ExceptionHandler.SwallowOnException(() {// check local cache first...if (_pendingCookies.TryGetValue(cookieName, out CachedCookie cookie)){// dont retrieve a deleted cookieif (cookie.IsDeleted)return default(T);return isBase64 ? Newtonsoft.Json.JsonConvert.DeserializeObjectT(cookie.Value.FromBase64String()): Newtonsoft.Json.JsonConvert.DeserializeObjectT(cookie.Value);}if (_httpContext.Request.Cookies.TryGetValue(cookieName, out string cookieValue))return isBase64 ? Newtonsoft.Json.JsonConvert.DeserializeObjectT(cookieValue.FromBase64String()): Newtonsoft.Json.JsonConvert.DeserializeObjectT(cookieValue);return default(T);});
}
在讨论之前让我们先看一下这些内容ExceptionHandler。我们的Get方法首先询问我们的pendingCookies字典是否有与键匹配的东西。如果有它将询问我们是否已对其进行标记IsDeleted。如果我们有一个并且未被删除那么我们继续将其反序列化为请求的对象类型并且可选地我们需要首先从base64对其进行解码。如果我们在缓存中没有它的本地副本那么我们继续看是否HttpContext.Request.Cookies具有它并且像我们的本地缓存一样可以选择在最终反序列化之前从base64解码。现在为什么我要对它进行base64编码从本质上讲我并不是要“保护”我的cookie免受窥视但是如果我有一个非常复杂的对象我要写出一个cookie我想对其进行分解。对象的JSON字符串表示形式可能非常笨拙。说到base64编码…这些是我在StringExtensions.cs文件中添加的几个扩展方法。干得好public static class StringExtensions
{public static string FromBase64String(this string value, bool throwException true){try{byte[] decodedBytes System.Convert.FromBase64String(value);string decoded System.Text.Encoding.UTF8.GetString(decodedBytes);return decoded;}catch (Exception ex){if (throwException)throw new Exception(ex.Message, ex);elsereturn value;}}public static string ToBase64String(this string value){byte[] bytes System.Text.ASCIIEncoding.UTF8.GetBytes(value);string encoded System.Convert.ToBase64String(bytes);return encoded;}
}
好吧这是什么ExceptionHandler.SwallowOnException法术我本可以使用该try {} catch {}块但这是一个用例其中我100可以接受失败只是因为存在cookie而已因为cookie根本就不存在。现在……如果您深入研究该处理程序的代码您会发现它仍在执行try / catch块我只是对其进行了抽象。让我向您证明这一点。异常处理程序public static class ExceptionHandler
{public static T SwallowOnExceptionT(FuncT func){try{return func();}catch{return default(T);}}
}
Set嘿我们很酷可以加载cookie但是如果我们不能创建cookie那不是很有用对吧让那部分起作用。public void SetT(string cookieName, T data, DateTimeOffset? expiry null, bool base64Encode false)where T : class
{// info about cookieoptionsCookieOptions options new CookieOptions(){Secure _httpContext.Request.IsHttps};if (expiry.HasValue)options.Expires expiry.Value;if (!_pendingCookies.TryGetValue(cookieName, out CachedCookie cookie))cookie Add(cookieName);// always set options and value;cookie.Options options;cookie.Value base64Encode? Newtonsoft.Json.JsonConvert.SerializeObject(data).ToBase64String(): Newtonsoft.Json.JsonConvert.SerializeObject(data);
}
创建Cookie时我们需要设置一些信息。我在这里几乎没有内容但我强烈建议您阅读CookieOptions[4]。不设置Expires将默认为“会话” cookie。如果您将Google Chrome浏览器用于“始终打开”模式或所谓的“笨拙”则它们将无法正常工作。在这里的代码中我们将查看是否已经有一个待处理的Cookie实例如果没有则添加一个实例。一分钟后我将介绍该方法。在获得cookie实例之后我们将附加选项并编写可选的以base64编码的值。Add现在让我们看一下该方法。protected CachedCookie Add(string cookieName)
{var cookie new CachedCookie{Name cookieName};_pendingCookies.Add(cookieName, cookie);return cookie;
}
很基本的东西。我们只是放宽信任我们可以添加它并添加它。该方法没有公开所以我相信我不必先检查字典。如果您对此不满意请随时进行修改。删除Cookie在某个时候我们将要删除Cookie对吗我们希望确保对同一cookie的后续查询都知道它已被删除正如我们在Get调用中所看到的那样。为了使它正常工作我们需要本地缓存来跟踪它。void ICookieService.Delete(string cookieName)
{Delete(cookieName);
}
protected CachedCookie Delete(string cookieName)
{if (_pendingCookies.TryGetValue(cookieName, out CachedCookie cookie))cookie.IsDeleted true;else{cookie new CachedCookie{Name cookieName,IsDeleted true};_pendingCookies.Add(cookieName, cookie);}return cookie;
}
在上面的代码中我们具有接口Delete方法和类Delete方法它们都具有相同的签名。我可以给他们起个不同的名字但我真的不想这么做。但是为了防止编译器报错我们必须将接口方法设为显式接口调用。我们只需将该调用传递到我们的类实例方法中。进入类实例delete方法后我们将查看是否已经有一个暂挂实例如果有请将其标记为已删除。如果没有我们将其添加到缓存中并标记为已删除。GetOrSet 有时您希望Cookie不管存在如何但是如果已经存在那么您就想获得它的价值。一个用例是如果您要加载cookie如果存在或设置默认值。在我工作过的一个站点上我们有一个适合该用例的“行程计划器”。我想知道他们的详细信息如果有的话否则我将设置一些默认值以便其余的会话体验基于相同的信息。设置非常简单public T GetOrSetT(string cookieName, FuncT setFunc, DateTimeOffset? expiry null, bool isBase64 false)where T : class
{T cookie GetT(cookieName, isBase64);if (cookie ! null)return cookie;T data setFunc();Set(cookieName, data, expiry, isBase64);return data;
}
如果cookie存在我们得到它。如果没有我们将其设置。十分简单。输出如果我们从不将其写回响应中那么以上所有代码实际上都没有关系对吗还记得在context.Response.OnStarting我们告诉服务期间在中间件中执行的服务WriteToResponse吗让我们现在实际做点什么public void WriteToResponse(HttpContext context)
{foreach (var cookie in _pendingCookies.Values){if (cookie.IsDeleted)context.Response.Cookies.Delete(cookie.Name);elsecontext.Response.Cookies.Append(cookie.Name, cookie.Value, cookie.Options);}
}
我们重复我们的每一个悬而未决的饼干和要么Delete或者Append他们根据我们的缓存值。现在我们只写出每个cookie的一个副本而不是我们在本文开头介绍的经典ASP.NET崩溃。与测试代码一起实现GitHub上的代码在HomeController中有一个相当蹩脚的小演示。接下来是一些单元测试。在发布一些代码之前我想回顾一下我的BaseTest.cs工作方式。我可以坦率地说应该有但是由于我从生产代码中复制了这个代码而这个代码还有其他问题所以我没有使用DotNetCore服务集合。相反BaseTest依赖于UnityContainer。对于我而言这是设置依赖项引擎的一种非常简单的方法。你想怎么嘲笑就怎么嘲笑吧。随之而来的将是CookieServiceTests类的一大堆垃圾。该Initialize方法设置了每个测试将要使用的内容然后每个单独的测试都设置了自己的场景。如何使用该服务应该变得显而易见并希望为您提供一些如何在自己的项目中使用该服务的想法。[TestClass]
public class CookieServiceTests : BaseTest
{IHttpContextAccessor _httpContextAccessor;HttpContext _httpContext;CookieService _target;[TestInitialize]public void Initialize(){_httpContextAccessor Substitute.ForIHttpContextAccessor();_httpContext new DefaultHttpContext();_httpContextAccessor.HttpContext.Returns(_httpContext);Container.RegisterInstance(_httpContextAccessor);_target Container.ResolveCookieService();}[TestMethod]public void CookieService_SetCookie_Success(){CookieFake cookie new CookieFake { TestProperty 25, TestPropertyString blah };_target.Set(fakecookie, cookie);CookieFake cachedCookie _target.GetCookieFake(fakecookie);Assert.IsNotNull(cachedCookie);Assert.AreEqual(cookie.TestProperty, cachedCookie.TestProperty);Assert.AreEqual(cookie.TestPropertyString, cachedCookie.TestPropertyString);}[TestMethod]public void CookieService_SetCookie_StringOnly_Success(){string value Im a cookie value;_target.Set(fakecookie, value);string result _target.Getstring(fakecookie);Assert.IsFalse(string.IsNullOrWhiteSpace(result));Assert.AreEqual(value, result);}[TestMethod]public void CookieService_SetCookie_Base64_Success(){CookieFake cookie new CookieFake { TestProperty 25, TestPropertyString blah };_target.Set(fakecookie, cookie, base64Encode: true);CookieFake cachedCookie _target.GetCookieFake(fakecookie, true);Assert.IsNotNull(cachedCookie);Assert.AreEqual(cookie.TestProperty, cachedCookie.TestProperty);Assert.AreEqual(cookie.TestPropertyString, cachedCookie.TestPropertyString);}[TestMethod]public void CookieService_GetOrSetCookie_SetsCookie_Success(){FuncCookieFake createCookie () {return new CookieFake { TestProperty 25, TestPropertyString blah };};var cookie _target.GetOrSetCookieFake(fakecookie, createCookie);Assert.IsNotNull(cookie);Assert.AreEqual(cookie.TestProperty, 25);}[TestMethod]public void CookieService_GetOrSetCookie_GetsCookie_Success(){CookieFake cookie new CookieFake { TestProperty 25, TestPropertyString blah };_target.Set(fakecookie, cookie);FuncCookieFake createCookie () {return new CookieFake { TestProperty 55, TestPropertyString blah2 };};var retrievedCookie _target.GetOrSetCookieFake(fakecookie, createCookie);Assert.IsNotNull(retrievedCookie);Assert.AreEqual(retrievedCookie.TestProperty, cookie.TestProperty);Assert.AreEqual(retrievedCookie.TestPropertyString, cookie.TestPropertyString);}[TestMethod]public void CookieService_GetCookie_Fail(){CookieFake cachedCookie _target.GetCookieFake(fakecookie);Assert.IsNull(cachedCookie);}[TestMethod]public void CookieService_GetCookie_Base64_Fail(){CookieFake cookie new CookieFake { TestProperty 25, TestPropertyString blah };_target.Set(fakecookie, cookie);CookieFake cachedCookie _target.GetCookieFake(fakecookie, true);Assert.IsNull(cachedCookie);}
}
public class CookieFake
{public int TestProperty { get; set; }public string TestPropertyString { get; set; }
}
结论DotNetCore Web应用程序中的Cookie管理并不是一件复杂的事情但是很容易使效率低下。我们通过引入CookieService和中间件研究了一种确保响应尽可能干净的方法。今天发布的所有代码都可以在我的GitHub上找到[5]。我鼓励您查看整个项目查看我在Web应用程序中蹩脚的示例我相信你能从中学到有用的知识。References[1] 原文链接: https://www.seeleycoder.com/blog/cookie-management-asp-net-core/[2] 我的GitHub上找到: https://github.com/fooberichu150/CookieService[3] Invoke方法中: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?viewaspnetcore-2.1#service-lifetimes[4] 您阅读CookieOptions: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.cookieoptions?viewaspnetcore-2.1[5] 我的GitHub上找到: https://github.com/fooberichu150/CookieService
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/913306.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!