0
0
Rest-apiHow-ToBeginner · 3 min read

How to Use Content Negotiation in REST APIs

Content negotiation in REST APIs lets the server choose the best response format based on the client's Accept header. You use Accept to specify preferred formats like application/json or application/xml, and the server responds accordingly.
📐

Syntax

Content negotiation uses the Accept HTTP header sent by the client to tell the server which response formats it prefers. The server reads this header and returns the response in one of the requested formats if supported.

Key parts:

  • Accept: Header sent by client listing preferred media types.
  • Content-Type: Header sent by server indicating the format of the response.
  • Server logic to check Accept and choose response format.
http
GET /resource HTTP/1.1
Host: example.com
Accept: application/json, application/xml;q=0.9

HTTP/1.1 200 OK
Content-Type: application/json
{
  "key": "value"
}
💻

Example

This example shows a simple REST API endpoint in Python using Flask that returns data in JSON or XML depending on the Accept header sent by the client.

python
from flask import Flask, request, Response
import json

app = Flask(__name__)

data = {'message': 'Hello, world!'}

@app.route('/greet')
def greet():
    accept = request.headers.get('Accept', '')
    if 'application/xml' in accept:
        xml_response = f"<response><message>{data['message']}</message></response>"
        return Response(xml_response, mimetype='application/xml')
    # Default to JSON
    return Response(json.dumps(data), mimetype='application/json')

if __name__ == '__main__':
    app.run(debug=True)
Output
Running the server. When client sends Accept: application/json, response is JSON; when Accept: application/xml, response is XML.
⚠️

Common Pitfalls

Common mistakes when using content negotiation include:

  • Ignoring the Accept header and always returning one format.
  • Not setting the correct Content-Type header in the response.
  • Failing to handle cases where the client requests unsupported formats.
  • Not providing a default format when Accept is missing or invalid.
python
from flask import Flask, request, Response
import json

app = Flask(__name__)

data = {'message': 'Hello, world!'}

@app.route('/greet')
def greet():
    accept = request.headers.get('Accept', '')
    # Wrong: ignoring Accept header
    # return Response(json.dumps(data), mimetype='application/json')

    # Right: check Accept and respond accordingly
    if 'application/xml' in accept:
        xml_response = f"<response><message>{data['message']}</message></response>"
        return Response(xml_response, mimetype='application/xml')
    elif 'application/json' in accept or '*/*' in accept or accept == '':
        return Response(json.dumps(data), mimetype='application/json')
    else:
        # Unsupported media type
        return Response('Unsupported Media Type', status=415)

if __name__ == '__main__':
    app.run(debug=True)
📊

Quick Reference

Header/ConceptPurposeExample Value
AcceptClient tells server preferred response formatsapplication/json, application/xml;q=0.9
Content-TypeServer tells client the response formatapplication/json
q-valueQuality factor to rank preferencesapplication/xml;q=0.8
Default responseFormat server uses if no matchapplication/json

Key Takeaways

Use the Accept header to let clients specify preferred response formats.
Always set the Content-Type header in your server responses to match the format.
Provide a default response format if the Accept header is missing or unsupported.
Handle unsupported media types by returning a 415 status code.
Test your API with different Accept headers to ensure correct content negotiation.