0

I am currently working on a website with a "shopping cart", when you open the shopping cart page, it creates a cookie with an ID, then this user is "registered" into database as if they signed up, but lacks some information like name, a proper username (instead adds a GUID) etc. so when they sign up, instead of creating a new user, this user in database gets updated and everything in shopping cart is already linked to this new account.

Problem with this is, this ID is only assigned if and only if user enters the link where shopping cart is, not any other page of website.

I can simply add this cookie creating to every single page in the website but instead of that, is there a way to make it work in any page of the website?

Below is a sample from function in Controller I use to create a cookie.

HttpCookie cookie = Request.Cookies.Get("UserId");
if (cookie == null)
{
    string cookieValue = Guid.NewGuid().ToString();
    Customer customer = new Customer();
    customer.UserName = cookieValue;
    if (SqlQuery.InsertGuest(customer))
    {
        HttpCookie userIdCookie = new HttpCookie("userId");
        userIdCookie.Value = cookieValue;
        userIdCookie.Expires = DateTime.Now.AddDays(90);
        Response.SetCookie(userIdCookie);
    }
}

Do I have to add this to every page controller of the website? Or is there an alternative?

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Tolga Açış
  • 168
  • 1
  • 16
  • Use https://msdn.microsoft.com/en-us/library/dd381609(v=vs.100).aspx. – Anis Alibegić Feb 06 '18 at 14:44
  • I don't understand what exactly to use here? Some example code still shows specific controllers. What I am trying to do is execute a function no matter what page user opens in the website and set a cookie if none exists yet. For example user can open www.website.com/about or www.website.com/contact as their first ever page on that website and without a "CreateCookie" to each and every controller of every view, I want to create a cookie. – Tolga Açış Feb 06 '18 at 14:49
  • You can create an action filter. Something like `[HttpGet]`, `[HttpPost]` and etc. Those are action filters. So, if you create an action filter that does the work you mentioned, you can add it at the controller level and it will be executed for every action in that controller. Just place it above your controller. `[GuestCookie]` above `public controller HomeController : Controller...` for example. – Anis Alibegić Feb 06 '18 at 14:52
  • But doesn't that mean I will still have to add [GuestCookie] to every controller for every view? I think I found another solution anyway, this seems like the opposite of what I'm asking. – Tolga Açış Feb 06 '18 at 14:59
  • Action filters have nothing to do with views. You would need to add them to all controllers. But instead, you can create BaseController and that action filter to only one controller and every other controller would inherit that base controller. – Anis Alibegić Feb 06 '18 at 15:01
  • @Spectarion or you can add a filter globally via the RegisterGlobalFilters method (in the FilterConfig.cs file). Easier and neater than inheriting from Controller, IMO. e.g. `filters.Add(new MyActionFilterAttribute());` – ADyson Feb 06 '18 at 16:09
  • That's also an option but what with the areas like admin, manager and etc..? How to exclude those areas? – Anis Alibegić Feb 06 '18 at 16:12
  • @ADyson turned off my PC for today, will try tomorrow. – Tolga Açış Feb 06 '18 at 16:14
  • @Spectarion it's not mentioned in the question whether such areas even exist. They may not exist, or might be part of another "admin" GUI app. It's quite common these days to separate such administration functionality because it's used by a completely different set of people, and often deployed at lower scale or only on an intranet or something. Even so, if those areas were there, setting a cookie containing the current user ID would seem to do no harm in those cases, even if it's not used. The filter could always contain a check about the context, too, if it mattered. – ADyson Feb 06 '18 at 16:29
  • 1
    @Spectarion You don't need to add them to all controllers. If you add it to the global filter list, then it will run for every controller action. – Fran Feb 06 '18 at 18:23

1 Answers1

4

Do I have to add this to every page controller of the website? Or is there an alternative?

Yes there is an alternative. Use a globally registered action filter.

Action Filter

public class MyCookieFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpCookie cookie = filterContext.HttpContext.Request.Cookies.Get("UserId");
        if (cookie == null)
        {
            string cookieValue = Guid.NewGuid().ToString();
            Customer customer = new Customer();
            customer.UserName = cookieValue;
            if (SqlQuery.InsertGuest(customer))
            {
                HttpCookie userIdCookie = new HttpCookie("userId");
                userIdCookie.Value = cookieValue;
                userIdCookie.Expires = DateTime.Now.AddDays(90);
                filterContext.HttpContext.Response.SetCookie(userIdCookie);
            }
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Put anything here you want to run after the action method (or leave empty)
    }
}

Usage

Registering the filter globally in FilterConfig.cs makes it run before and after every action method call.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new MyCookieFilter());
        filters.Add(new HandleErrorAttribute());
    }
}

NOTE: If you need to use dependency injection to provide your SqlQuery service to the filter, you can build an IFilterProvider as shown here to provide your filter through DI rather than registering it in the GlobalFilterCollection.

Another Alternative

Rather than writing a cookie yourself on every request, ASP.NET has a built-in Anonymous Identification Module that would make a good fit for an anonymous shopping cart feature.

Reference: Think twice about using session state

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Action Filter worked almost perfect, with one problem. No matter what link I click, it executes the ActionFilter I created called Cookies(), as intended, but problem is, let's say if I I go to, www.website.com/about, it redirects me to homepage after executing the function, to www.website.com itself. – Tolga Açış Feb 07 '18 at 07:46
  • That doesn't sound like something that would be caused by an action filter without any redirect code in it. Is the value of `filterContext.Result` null when the `OnActionExecuting` method finishes? If not null, there is another filter setting it somewhere (that you need to track down). If null, then I suspect it has to do with the action method itself. Try to track down the issue, and if you can't find it start a new SO question and post the code in from the action method and from any [application level event handlers](https://msdn.microsoft.com/en-us/library/fwzzh56s.aspx) . – NightOwl888 Feb 07 '18 at 08:05
  • 1
    Okay found the problem, turns out I added cookie creating into Executed, not Executing. Solved and solution works perfect, thank you. – Tolga Açış Feb 07 '18 at 08:33