0

I'm making a shopping cart using ASP.NET Core Web API and react. When testing what was done in Swagger UI and Postman, everything works correctly, but when switching to react, problems begin. Each time when I add product in swagger/postman, all products are added with their ShoppingCartId that is related to the current session. But when I tested it with React, after each added product a new ShoppingCartId is created, which shouldn't happen. Below is the code and result:

Result in DB

Session Configuration:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped(sc => ShoppingCart.GetShoppingCart(sc));

services.AddDistributedMemoryCache();
services.AddSession(options => {
            options.IdleTimeout = TimeSpan.FromMinutes(3600);
            options.Cookie.HttpOnly = true;
            options.Cookie.IsEssential = true; 
        });

ShoppingCart class:

public class ShoppingCart
{
    public AppDbContext _context { get; set; }
    public string ShoppingCartId { get; set; }
    public List<ShoppingCartItem> ShoppingCartItem { get; set; }

    public ShoppingCart(AppDbContext context)
    {
        _context = context;
    }

    public static ShoppingCart GetShoppingCart (IServiceProvider services)
    {
        ISession session = services.GetRequiredService<IHttpContextAccessor>()?.HttpContext.Session;
        var context = services.GetService<AppDbContext>();

        string cardId = session.GetString("CardId") ?? Guid.NewGuid().ToString();
        session.SetString("CardId", cardId);

        return new ShoppingCart(context) { ShoppingCartId = cardId };
    }

    public List<ShoppingCartItem> GetShoppingCartItems()
    {
        return ShoppingCartItem ?? (ShoppingCartItem = _context.ShoppingCartItem.Where(n => n.ShoppingCartId ==
        ShoppingCartId).Include(n => n.Book).ToList());
    }

    public void AddItemToCart(Book book)
    {
        var shoppingCartItem = _context.ShoppingCartItem.FirstOrDefault(n => n.Book.Id == book.Id &&
        n.ShoppingCartId == ShoppingCartId);

        if(shoppingCartItem == null)
        {
            shoppingCartItem = new ShoppingCartItem()
            {
                ShoppingCartId = ShoppingCartId,
                Book = book,
                Amount = 1
            };
            _context.ShoppingCartItem.Add(shoppingCartItem);
        }
        else
        {
            shoppingCartItem.Amount++;
        }
        _context.SaveChanges();
    }
}

Controller:

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private readonly ShoppingCart _shoppingCart;
    private readonly IOrderService _orderService;
    public OrderController(ShoppingCart shoppingCart, IOrderService orderService)
    {
        _shoppingCart = shoppingCart;
        _orderService = orderService;
    }

   [HttpGet("get-shopping-cart")]
    public IActionResult GetShoppingCart()
    {
        var items = _shoppingCart.GetShoppingCartItems();
        _shoppingCart.ShoppingCartItem = items;

        var responce = new ShopCartVM()
        {
            ShoppingCartItem = items,
            ShopCartTotal = _shoppingCart.GetShoppingCartTotal(),
            TotalItems = _shoppingCart.GetShoppingCartItemsSummary()
        };
        return Ok(responce);
    }

    [HttpPost("add-item-to-cart")]
    public async Task<IActionResult> AddItemToShoppingCart(int id)
    {
        var item = await _bookService.GetByIdShopAsync(id);

        if (item != null)
        {
            _shoppingCart.AddItemToCart(item);
        }
        return RedirectToAction(nameof(GetShoppingCart));
    }
}

React

 export const addProduct = (productId: number) => async (dispatch: AppDispatch) => {
    try {
        const request = new XMLHttpRequest();
        request.open("POST", `https://localhost:44307/api/Order/add-item-to-cart?id=${productId}`)
        request.responseType = "text"
        request.onload = function () {
            const response = request.response;
            console.log(response)
        };
        request.send();
    } catch (e) {
        console.log(e)
    }
}

Update: I think the problem is that in React a new session is generated after each action, which does not happen in Swagger. What could be the reason for this and how to fix it?

Below is the screenshots with changes and static session:

.AspNetCore.Session React

.AspNetCore.Session React

.AspNetCore.Session Swagger

.AspNetCore.Session Swagger

Adrian
  • 9
  • 5
  • Could you please inform us, how you are getting the session in the React App from WebAPI and how you are keeping it? If you are not keeping it in your app then it could possibly lead to an issue you are facing currently. You could try to store the session values in the Cookies. See whether it helps to get the same shopping id. – Deepak-MSFT Apr 25 '22 at 06:35
  • Hi Deepak, thanks for your reply. I'm not keeping it in app. To be honest, I don't know how to do it. I thought this was not necessary, because after each action with the shoppingcart, it is checked if there is a session and if it is not, I create a new id. This can be seen in `GetShoppingCart`. I thought when creating a session, it exists until it is over. In Swagger tests or MVC this worked, but now I just have no idea what to do. I would be very grateful if you can help me. – Adrian Apr 25 '22 at 10:14

2 Answers2

1

You can remove app.UseCookiePolicy() . This entry will block cookies until you confirm that you agree to receive them. Also provide your cookie name options.Cookie.Name = "YourName". In options.Cookie.HttpOnly = true, you need to change the value to false, otherwise the browser will not be able to read the cookie. Also specify options.Cookie.SameSite = SameSiteMode.None to set the cookie to your react app.

I think that something among all this should help you, correct me if I have indicated something incorrectly.

Nnnn
  • 26
  • 1
0

I do believe that before you put an item to your cart you obtain a cart from the get-shopping-cart method. You can contain here Id of the shopping cart and set it somewhere on your frontend side, then you can pass productId and cartId to a add-item-to-cart endpoint and have your cartId always contained in the request.

Also I found this What is the best way to manage a user's session in React? question which may help you as well.

Btw. On the GetShoppingCart method you call it cardId instead of carrId> Don't you have a typo?

Hantick
  • 63
  • 7
  • `GetShoppingCart` is ok. I've updated the question, I believe that react generates a new session after each action, which normally shouldn't happen. – Adrian Apr 25 '22 at 04:39