python 36 lines · 7 steps

Content negotiation in a Flask Blueprint

A single Flask route serves the same article as JSON or HTML based on the client's Accept header.

Explained by highlit
1from flask import Blueprint, render_template, jsonify, request, abort
2 
3from .models import Article
4 
5bp = Blueprint("articles", __name__)
6 
7 
8@bp.route("/articles/<slug>")
9def show(slug):
10 article = Article.query.filter_by(slug=slug, published=True).first()
11 if article is None:
12 abort(404)
13 
14 best = request.accept_mimetypes.best_match(
15 ["application/json", "text/html"],
16 default="text/html",
17 )
18 
19 if (
20 best == "application/json"
21 and request.accept_mimetypes[best]
22 >= request.accept_mimetypes["text/html"]
23 ):
24 payload = {
25 "slug": article.slug,
26 "title": article.title,
27 "body": article.body,
28 "author": article.author.name,
29 "published_at": article.published_at.isoformat(),
30 "tags": [tag.name for tag in article.tags],
31 }
32 response = jsonify(payload)
33 response.headers["Vary"] = "Accept"
34 return response
35 
36 return render_template("articles/show.html", article=article), 200, {"Vary": "Accept"}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1One endpoint can serve multiple representations by inspecting the Accept header instead of splitting into separate URLs.
  2. 2Setting the Vary header tells caches that responses differ by request headers, preventing wrong cached formats.
  3. 3Guarding queries with published=True and abort(404) keeps unpublished or missing content invisible.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Content negotiation in a Flask Blueprint — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code