Customers can now review a product: a number of stars from 1 to 5 and a short text. A review belongs to one product, so its path goes under the product's path: /products/1/reviews are the reviews of product 1. This is a nested path, and the id in the middle is a path parameter, read with {id:int} as in GET /products/1.
The reviews are already in the file, in the dictionary reviews: each key is a product id, and each value is the list of that product's reviews. A product with no reviews has no key, so reviews.GetValueOrDefault(id, new List<Dictionary<string, object>>()) gives its list, or an empty one. The same dictionary of lists, with other values:
var tags = new Dictionary<int, List<string>> { [1] = new List<string> { "sale", "new" } };
Console.WriteLine(tags.GetValueOrDefault(1, new List<string>()).Count); // 2
Console.WriteLine(tags.GetValueOrDefault(2, new List<string>()).Count); // 0
tags[2] = tags.GetValueOrDefault(2, new List<string>());
tags[2].Add("gift");
Console.WriteLine(string.Join(", ", tags[2])); // gift
One path can have a route for each method: GET reads a product's reviews, and POST adds one and answers 201 with it. Both answer 404 when no product has the id. The GET route is already in your file; until a POST route exists, ASP.NET Core answers a POST on this path with 405 Method Not Allowed. Your code must answer these requests, in this order:
GET /products/1/reviews → 200 [{"stars": 5, "text": "Great mug"}, {"stars": 4, "text": "A bit small"}]
GET /products/4/reviews → 200 []
GET /products/99/reviews → 404 {"error": "Product not found"}
POST /products/2/reviews {"stars": 4, "text": "Nice paper"} → 201 {"stars": 4, "text": "Nice paper"}
GET /products/2/reviews → 200 [{"stars": 4, "text": "Nice paper"}]
POST /products/1/reviews {"stars": 3, "text": "The handle gets hot"} → 201 {"stars": 3, "text": "The handle gets hot"}
GET /products/1/reviews → 200 [{"stars": 5, …}, {"stars": 4, …}, {"stars": 3, "text": "The handle gets hot"}]
POST /products/99/reviews {"stars": 5, "text": "Love it"} → 404 {"error": "Product not found"}
GET /products/1 → 200 {"id": 1, "name": "Mug", "price": 12, "category": "kitchen"}
Your task: below the GET route for /products/{id:int}/reviews, add a MapPost route for the same path. It reads the review from the body, adds it at the end of the product's list and answers 201 with it, or answers 404 when the product doesn't exist.
Assignment
Follow the Coding Tutorial to add the POST route on the nested path, below the GET route that's already there.
Hint
Find the product first, with the loop and if of the GET route. GetValueOrDefault never fails on a missing key: store its list under reviews[id], then Add the review to reviews[id]. The lambda compiles again only once the 404 after the loop is in.
Free plan: 30 tutor messages. PRO is unlimited.
