Start learning free

LESSON 172 OF 173 · BACKEND: YOUR OWN API

Short Links: Only Real URLs

Practice

Right now your shortener shortens anything it gets, and reading a missing "url" with GetProperty throws an exception, which the app answers with a 500. Short links are clicked by strangers, so the app should check what it gets first. Bad input is turned away with 400 Bad Request, before anything is counted or stored.

Two tools do the checking. A JsonElement's TryGetProperty reads a key without crashing: it returns false when the key is missing, and ? : then picks a default instead. A string's StartsWith method tells you whether it begins with a given text. Here is the same idea on another route, a list of tags that only accepts names starting with #:

app.MapPost("/tags", (JsonElement data) => {
    var name = data.TryGetProperty("name", out var value) ? value.GetString() : "";
    if (!name.StartsWith("#")) {
        return Results.Json(new { error = "A tag starts with #" }, statusCode: 400);
    }
    tags.Add(name);
    return Results.Json(new { name = name }, statusCode: 201);
});

Only addresses that start with http:// or https:// are web pages to send people to. Without that start, a browser reads www.python.org as a page of your own app, and ftp: or javascript: addresses don't belong behind a link strangers click. One more rule: the same URL sent twice gets the code it already has, with 200 OK, because nothing new was created. These are the test requests, in order:

POST /links  {"url": "https://www.python.org/downloads/"}  →  201  {"code": "1", "url": "https://www.python.org/downloads/"}
POST /links  {"url": "ftp://files.example.com/report.pdf"}  →  400  {"error": "The URL must start with http:// or https://"}
POST /links  {"url": "javascript:alert(1)"}  →  400  {"error": "The URL must start with http:// or https://"}
POST /links  {"url": "www.python.org"}  →  400  {"error": "The URL must start with http:// or https://"}
POST /links  {}  →  400  {"error": "The URL must start with http:// or https://"}
POST /links  {"url": "http://example.com/old-page"}  →  201  {"code": "2", "url": "http://example.com/old-page"}
POST /links  {"url": "https://www.python.org/downloads/"}  →  200  {"code": "1", "url": "https://www.python.org/downloads/"}
GET /links/3/stats  →  404  {"error": "Link not found"}
GET /2  →  302  Location: http://example.com/old-page

Your task: in the /links route, read the URL with TryGetProperty and the default "", and answer 400 with the error above when it starts with neither http:// nor https://. Then, before making a new code, loop over links.Keys: if a stored link has the same URL, answer its code and URL with 200.


Assignment
Follow the Coding Tutorial to make the /links route accept only real web addresses and give a URL it already knows its old code.


Hint
Both checks go above counter += 1, so a refused or repeated URL uses up no number: that is why GET /links/3/stats answers 404. links.Keys gives the codes; call the loop variable existing, because the name code is taken further down the route. Read the stored URL as a string before comparing it with ==.


STEP 1 OF 6
    Ctrl + Enter to check