Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ? valid, but the code we use can't be used in a real application.

The application/problem+json Content-Type

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

The application/problem+json Content-Type

Sometimes, there are clear rules in the API world. When we create a new resource, returning a 201 status code is the right thing to do.

But other times, there aren’t clear rules. What we’re working on right now is a good example: there’s no standard for how API error responses should look.

Our response has type, title, and errors fields. And I didn’t invent this: it’s part of a young, potential standard called API Problem, or Problem Details. When we google for it, we find an RFC document of course! Actually, this is technically an “Internet Draft”: a work-in-progress document that may eventually be a standard. If you use this, then you should understand that it may change in the future or be discarded entirely for something different.

But in the API world, sometimes we can choose to follow a draft like this, or nothing at all. In other words, we can choose to make our API consistent with at least some other API’s, or consistent with noone else.

Oh, and when you’re reading one of these documents, make sure you’re on the latest version - they’re updated all the time.

Dissecting API Problem

If we read a little bit, we can see that this standard outlines a response that typically has a type field, a title and sometimes a few others. The type field is the internal, unique identifier for an error and the title is a short human description for the type. If you look at our type and title values, they fit this description pretty well.

And actually, the type is supposed to be a URL that I can copy into my browser to get even more information about the error. Our value is just a plain string, but we’ll fix that later.

The spec also allows you to add any extra fields you want. Since we need to tell the user what errors there are, we’ve added an errors field.

This means that our error response is already following this spec. Yay! And since someone has already defined the meaning of some of our fields, we can link to this document as part ouf or API’s docs FTW!

Media Types and Structure Versus Semantics

Of course right now, there’s no way an API client would know that we’re leveraging this draft spec, unless they happen to recognize the structure. It would be much better if the response somehow screamed “I’m using the Problem Details spec!”.

And this is totally possible by sending back a Content-Type header of application/problem+json. This says that the actual format is json, but that a client can find out more about the underlying meaning or semantics of the data by researching the application/problem+json Content-Type.

So the json part tells us how to parse the document. The problem part give us a hint on how to find out the human meaning of its data.

This is called the media type of the document, and if you google for IANA Media Types, you’ll find a page of all of the official recognized types. You can see that there are a lot of media types that end in +json, like one for expressing calendar data. If you were sending calendar data, you might choose to use this format. Why? Because it would mean you’re following a standard that is already documented, and whose structure people spent a lot of time thinking about.

Right now, I just want you to be aware that this exists, and that a lot of people invest a lot of time into answering questions like: “If 2 machines are sending calendar data, how should it be structured?”.

Our application/problem+json actually isn’t in this list, because it’s just a draft.

Setting the Content-Type Header

But even still, we want people to know our error response is using this media type. First, let’s update the test to look for this Content-Type header:

# features/api/programmer.feature
# ...

Scenario: Validation errors
  # all the current scenario lines
  # ...
  And the "Content-Type" header should be "application/problem+json"

Next, add the header to our response. We’ve added plenty of response headers already, and this is no different:

// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...

private function handleValidationResponse(array $errors)
{
    // ...

    $response = new JsonResponse($data, 400);
    $response->headers->set('Content-Type', 'application/problem+json');

    return $response;
}

When we try the tests, they still pass!

And now the client knows a bit more about our error response, without us writing even one line of documentation.

Leave a comment!

4
Login or Register to join the conversation
Thao L. Avatar
Thao L. Avatar Thao L. | posted 5 years ago

It's nice that this course uses a female narrator :)

Reply
Default user avatar
Default user avatar Martín Anacabe | posted 5 years ago

The standard "Problem Details for HTTP APIs" is released in March 2016.
REF: RFC 7807

Good Job!

Reply

Woh, that's awesome! It's about time! Thanks for posting this :)

Reply
Default user avatar
Default user avatar Martín Anacabe | weaverryan | posted 5 years ago

YW and congrats for this great tutorial.

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ? valid, but the code we use can't be used in a real application.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "silex/silex": "~1.0", // v1.3.2
        "symfony/twig-bridge": "~2.1", // v2.7.3
        "symfony/security": "~2.4", // v2.7.3
        "doctrine/dbal": "^2.5.4", // v2.5.4
        "monolog/monolog": "~1.7.0", // 1.7.0
        "symfony/validator": "~2.4", // v2.7.3
        "symfony/expression-language": "~2.4" // v2.7.3
    },
    "require-dev": {
        "behat/mink": "~1.5", // v1.5.0
        "behat/mink-goutte-driver": "~1.0.9", // v1.0.9
        "behat/mink-selenium2-driver": "~1.1.1", // v1.1.1
        "behat/behat": "~2.5", // v2.5.5
        "behat/mink-extension": "~1.2.0", // v1.2.0
        "phpunit/phpunit": "~5.7.0", // 5.7.27
        "guzzle/guzzle": "~3.7" // v3.9.3
    }
}
userVoice