Start learning free

LESSON 163 OF 173 · BACKEND: BACKEND WITH AI

The AI Shows Too Much in C#

Practice

An AI assistant wrote the label route, the text for the shop's shelf: GET /products/1/label answers {"label": "Mug (kitchen)"}. It put its code in a try, which is a good idea: when something fails, the route still answers. But look at what its catch sends back:

app.MapGet("/products/{id}/label", (int id, HttpRequest request) => {
    var sql = "SELECT name, category FROM products WHERE id = $id";
    try {
        var cmd = db.CreateCommand();
        cmd.CommandText = sql;
        cmd.Parameters.AddWithValue("$id", id);
        using var row = cmd.ExecuteReader();
        row.Read();
        var label = row.GetString(0) + " (" + row.GetString(1) + ")";
        return Results.Json(new { label = label });
    } catch (Exception error) {
        return Results.Json(new { error = error.Message, sql = sql, trace = error.StackTrace }, statusCode: 500);
    }
});

That answer goes to whoever made the request. It holds the error's text, the SQL query and the stack trace: the methods, files and lines of code that led to the crash. A customer can't do anything with it. An attacker can: table names, column names and your code are a free map for finding the next hole.

The rule: the details go to the log, which only you read, and the visitor gets a short safe message. Here it is on another route:

    } catch (Exception error) {
        app.Logger.LogError("order {Id} failed: {Message}", orderId, error.Message);
        return Results.Json(new { error = "Please try again later" }, statusCode: 500);
    }

A missing product is not a server error, so it answers 404, not 500. Your rewrite must give these answers (product 8 is a Gift card with no category, a bug nobody had found yet):

GET /products/1/label   →  200  {"label": "Mug (kitchen)"}
GET /products/6/label   →  200  {"label": "Teapot (kitchen)"}
GET /products/8/label   →  500  {"error": "Something went wrong on our side"}
GET /products/99/label  →  404  {"error": "Product not found"}

The check then reads your log through /_test/log, a route it adds only for the test, and expects one line: ERROR GET /products/8/label failed: The data is NULL at ordinal 1. This method can't be called on NULL values. Check using IsDBNull before calling.

Your task: rewrite the body of the label route's MapGet function so any error answers only {"error": "Something went wrong on our side"} with 500 and logs the method, the path and the error, while a missing product answers 404.


Assignment
Follow the Coding Tutorial to rewrite the label route with a try that builds the label and a catch that logs the details and answers a safe message.


Hint
The catch comes right after the try's closing brace: } catch (Exception error) {. The 404 check sits inside the try, right below the row line: row.Read() gives false when there is no row. Pass app.Logger.LogError the text "{Method} {Path} failed: {Message}", then the three values in order.


STEP 1 OF 12
    Ctrl + Enter to check

    Ai Leaky Errors in C#

    Keep going with C#1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free