25

I have a login page that goes off to the server gets a bunch of data, then I want to take some of that data and save it into a cookie using Blazor on the client.

So To start I have successfully injected IHttpContextAccessor. and for now in my Blazor function I have:

httpContextAccessor.HttpContext.Response.Cookies.Append("test", "ddd");

in debug when I hit the above line of code it errors with:

"Headers are read-only, response has already started."

Of course I will not be saving "test" with "ddd" in the cookie, I'm just trying to get a cookie to save at the moment.

2
  • 1
    An alternative is to use LocalStorage via this Blazor library. IMO, it's a more suitable client storage vehicle for Blazor apps as cookies are sent on every request whereas local storage is data available to your Blazor app to use as you see fit. Commented Jan 3, 2019 at 20:25
  • 2
    We are using local storage where we can, but for this bit, another piece of software that we interact with looks for info stored in a Cookie. This app needs to create it on behalf of the secondary piece of software until we get round to being able to change that. Commented Jan 4, 2019 at 7:51

5 Answers 5

25

You can use this way without any script:
First define this service:

using Microsoft.JSInterop; namespace MyProject.Utils { public interface ICookie { public Task SetValue(string key, string value, int? days = null); public Task<string> GetValue(string key, string def = ""); } public class Cookie : ICookie { readonly IJSRuntime JSRuntime; string expires = ""; public Cookie(IJSRuntime jsRuntime) { JSRuntime = jsRuntime; ExpireDays = 300; } public async Task SetValue(string key, string value, int? days = null) { var curExp = (days != null) ? (days > 0 ? DateToUTC(days.Value) : "") : expires; await SetCookie($"{key}={value}; expires={curExp}; path=/"); } public async Task<string> GetValue(string key, string def = "") { var cValue = await GetCookie(); if (string.IsNullOrEmpty(cValue)) return def; var vals = cValue.Split(';'); foreach (var val in vals) if(!string.IsNullOrEmpty(val) && val.IndexOf('=') > 0) if(val.Substring(0, val.IndexOf('=')).Trim().Equals(key, StringComparison.OrdinalIgnoreCase)) return val.Substring(val.IndexOf('=') + 1); return def; } private async Task SetCookie(string value) { await JSRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{value}\""); } private async Task<string> GetCookie() { return await JSRuntime.InvokeAsync<string>("eval", $"document.cookie"); } public int ExpireDays { set => expires = DateToUTC(value); } private static string DateToUTC(int days) => DateTime.Now.AddDays(days).ToUniversalTime().ToString("R"); } } 

You can set default expire by ExpireDays, and in SetValue() set null to days for use default or set to 0 for session or number of days.
Then import to _import.razor and add to service in Program.cs:

builder.Services.AddScoped<ICookie, Cookie>(); 

For use:

@inject ICookie cookie ... await cookie.SetValue("mytest20", "Hello Mohsen!"); _message = await cookie.GetValue("mytest20"); 

Enjoy it.

Updated by @jaap Comment.

Sign up to request clarification or add additional context in comments.

3 Comments

if(val.Substring(1, val.IndexOf('=') - 1).Trim().Equals(key, StringComparison.OrdinalIgnoreCase)) should be (val.Substring(0, val.IndexOf('=')).Trim().Equals(key, StringComparison.OrdinalIgnoreCase)) I think
Anyone using this service, please check @Jaap's comment.
Or range operator: if (val[..val.IndexOf('=')].Trim().Equals(key, StringComparison.OrdinalIgnoreCase)) { return val[(val.IndexOf('=') + 1)..]; }
24

You will have to use JS interop:

 public async static Task WriteCookieAsync(string name, string value, int days) { var test = await JSRuntime.Current.InvokeAsync<object>("blazorExtensions.WriteCookie", name, value, days); } 

Starting with ASP.NET Core 3.0.0-preview3 ([Discussion] Microsoft.Interop.JSRuntime.Current has been removed), the Current property is not available, so use the following code:

var test = await JSRuntime.InvokeAsync<string>("blazorExtensions.WriteCookie", name, value, days); 

Don't forget to inject IJSRuntime at the top:

@inject IJSRuntime JSRuntime 

And this JS:

window.blazorExtensions = { WriteCookie: function (name, value, days) { var expires; if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toGMTString(); } else { expires = ""; } document.cookie = name + "=" + value + expires + "; path=/"; } } 

2 Comments

I know it's still early but I'm praying not to have to always use JS interop everytime I want to do little things like this, this should be an out of the box feature. But thanks, that's helpful!
I agree, eventually this will be provided by community packages. See here: github.com/BlazorExtensions
7

If you want to avoid the external script dependency

await jsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{cookieValue}\"") 

Remains to properly escape the cookie value.

1 Comment

Perfect and concise solution, thank you. Do you have an example for "Remains to properly escape the cookie value."? Thank you.
3

With the help of the code snippet that MohsenB provided. I have created a NuGet package for it.

NuGet install:

Install-Package AltairCA.Blazor.WebAssembly.Cookie 

Program.cs

builder.Services.AddAltairCACookieService(options => { options.DefaultExpire = TimeSpan.FromMinutes(15); }); 

Example of use

Inject Service

@inject IAltairCABlazorCookieUtil _cookieUtil; 

Example 1

await _cookieUtil.SetValueAsync("cookieName", "value"); 

Comments

2

You can add javaScript explained by @Flores in_Host.cshtml inside <script> tag.

<script> window.blazorExtensions = { WriteCookie: function (name, value, days) { var expires; if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toGMTString(); } else { expires = ""; } document.cookie = name + "=" + value + expires + "; path=/"; } } </script> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.