Tutorials / Build a REST API with C# and .NET · Lesson 25 of 29

On this page

    The API So Far: Every Path

    The last four lessons each added a piece: a standard shape for errors, a way to create a book, a way to change and remove one, and the shop's own rules. That is a lot of pieces to hold in your head at once. This lesson adds nothing new. It puts the pieces side by side, so you can see the whole picture.

    Our goal: for any request to the books API, say which answer comes back and which part of the code gave it. There is no code to write and nothing to run. Every answer below was shown, run for real, in errors the professional way, create a book, update and delete and business rules and meaningful errors.

    Here is the route:

    1. A picture first: the answers a shop counter can give.
    2. The paths that work, one for each request.
    3. The paths that fail, and who can fix each one.
    4. The one shape that every failure shares.
    5. The questions a request passes through, in order.

    This lesson leaves out anything new. The next endpoint, the stock report, comes after it.


    Five answers at a counter

    Picture a customer at the counter of a bookshop, asking for a book. The person behind the counter can answer in a few ways:

    One question, five answers The customer asks for a book “Here it is.” “Sorry, I cannot read this form.” “We have no such title.” “We have it, but not that many.” “Sorry, our till has broken.”

    Our API gives these five kinds of answer. The rest of the lesson gives each one its status code.


    The paths that work

    Every request that works answers with a status in the 2xx family, and the status says what kind of success it was. The body is the data itself, with no wrapper, or nothing at all.

    Request What it does Answer
    GET /books List all books 200 OK, a list
    GET /books/{id} Read one book 200 OK, the book
    POST /books Create a book 201 Created, the new book, and a Location header with its address
    PUT /books/{id} Replace a book 204 No Content, no body
    DELETE /books/{id} Remove a book 204 No Content, no body
    POST /books/{id}/sell?quantity=N Sell copies of a book 204 No Content, no body

    Reading gives data back, so it answers 200. Creating gives back the new book and says where it lives, so it answers 201. Changing, removing and selling have nothing to report except "done", so they answer 204.


    The paths that fail

    A request can fail in a few different ways. The useful question about each one is: who can fix it? If the client can fix it, the status starts with 4. If only we can, it starts with 5.

    Answer What happened Who says it Who can fix it
    400 The request is wrong whatever is in the database. For example an id of abc, a body without a title, or a quantity of 0. The checks of [ApiController], before our method runs The client
    415 The body was sent without saying it is JSON. ASP.NET The client
    404 The request is fine, but the thing is not there. For example a book that does not exist, or a second DELETE of the same book. Our controller, when the service says "no such book"; ASP.NET for an address that does not exist The client, by asking for something else
    409 The request is fine and the book exists, but a rule of the shop says no. For example selling 5 copies when 3 are left. The service, which holds the rule; the controller turns its answer into the status The client, by changing the request
    500 Our own code failed and nobody planned for it. For example a database that is broken or missing. The global exception handler Only us

    To tell a 400 from a 409, use the question from the business rules lesson: would the answer be the same whatever is in the database? A quantity of 0 is wrong with any data, so it is a 400. Selling 5 copies is fine with a full shelf and wrong with a short one, so it depends on the data, and it is a 409.

    💡 Which status a rule gets is a choice. Some teams answer a rule with 400 or 422. We use 409, and keep to it, because the same kind of problem should always get the same answer.

    A real project has more paths than ours. Deleting a book that other records depend on is one, and it is also a 409 or a different design altogether, as the note in the update and delete lesson says. Our bookshop keeps to the five answers above.


    One shape for every failure

    Every failure above, from the 400 to the 500, comes back in the same shape, called Problem Details. Here is the 409 from the business rules lesson, as a reminder (the real body is one long line, shown with line breaks, and its traceId line is left out):

    HTTP/1.1 409 Conflict
    Content-Type: application/problem+json; charset=utf-8
    
    {
      "type": "https://bookshop.example/errors/insufficient-stock",
      "title": "Not enough copies in stock.",
      "status": 409,
      "detail": "Copies left of Refactoring: 3. You asked for 5."
    }
    

    The other failures have the same fields. A 400 adds an errors list that says which field was wrong. A 500 has only our own short sentence, and the details go to the log, so a user never sees a stack trace. The point of one shape is that the frontend learns it once: it reads the status, shows the title, and the code has no special case for each endpoint.


    How a request ends

    Put the failures in order and you get the questions a request passes through. The first "no" is the answer:

    The questions a request passes through Is the request well formed? Does the book exist? Do the shop’s rules allow it? Does our code run fine? Success: a 2xx answer 400 404 409 500 no no no no yes yes yes yes

    This picture is simplified. The checks do not happen in one place: the first one runs in the gate of [ApiController] before our method starts, the next two in the service and the controller, and the last is a safety net under everything. Keep the order, which is the useful part: a request is turned away at the first problem it has.


    Recap

    • Every answer the books API gives is one of five kinds: success, bad request, not found, a rule says no, or our code broke.
    • Success is in the 2xx family. Reading gives 200 with data, creating gives 201 with a Location, and changing, removing and selling give 204 with no body.
    • A 4xx failure is one the client can fix. A 5xx failure is ours to fix.
    • 400 is a request that is wrong whatever is in the database. 404 is a thing that is not there. 409 is a rule of the shop that says no. 500 is a crash nobody planned for.
    • To tell input from a rule, ask: would the answer be the same whatever is in the database?
    • Every failure has the same shape, Problem Details, so the frontend learns it once. A 500 shows only our own sentence, and the details go to the log.
    • A request ends at the first question it fails: well formed, exists, allowed, no crash.

    Questions to think about

    Each question has its answer folded away underneath. Guess first, then open it to check.

    1. What is the difference between a 4xx and a 5xx answer?
    Show answer

    A 4xx means the client can fix it by changing the request. A 5xx means the fault is on our side, and only we can fix it.

    1. How do you tell a 400 from a 409?
    Show answer

    Ask whether the answer would be the same whatever is in the database. If it would, the input is wrong and it is a 400. If it depends on the data, such as too few copies, a rule of the shop is saying no and it is a 409.

    1. Why does every failure come back in the same shape?
    Show answer

    So the frontend has to learn only one shape. It reads the same fields for every error and needs no special case for each endpoint.

    1. Why does a 500 show the client only a short sentence?
    Show answer

    The details of a crash are useful to a developer and to an attacker, and mean nothing to a customer. They go to the log, and the client gets a short message of ours.

    Next lesson: the stock report. The first endpoint whose answer is not a row of the table, so the API needs a class made for the answer.

    ← Business Rules and Meaningful ErrorsThe Stock Report →