Opened 8 weeks ago

Closed 6 weeks ago

Last modified 6 weeks ago

#36411 closed Bug (fixed)

MediaType.get_preferred_type ignores params

Reported by: magicfelix Owned by: Jake Howard
Component: HTTP handling Version: 5.2
Severity: Release blocker Keywords:
Cc: Jake Howard, magicfelix Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I use the following code to get the preferred content type:

media_types = ["text/vcard; version=4.0", "text/vcard; version=3.0", "text/vcard", "text/directory"]
print("Preferred type:", request.get_preferred_type(media_types))

A request using the following header …
Accept: text/vcard; version=3.0
… produces the following result
Preferred type: text/vcard; version=4.0

It should instead return text/vcard; version=3.0, as 4.0 was not included in the Accept header.

Change History (17)

comment:1 by David Sanders, 8 weeks ago

Perhaps someone can weigh in on the correct behaviour on matching mime types, but MediaType.match() only checks <main-type>/<sub-type> without consideration for the ; <params>:

    def match(self, other):
        if self.is_all_types:
            return True
        other = MediaType(other)
        return self.main_type == other.main_type and self.sub_type in {
            "*",
            other.sub_type,
        }

https://github.com/django/django/blob/d2732c30af28381f5a2ff1b08f754eeb7a6dfeca/django/http/request.py#L716-L719

comment:2 by David Sanders, 8 weeks ago

There is also this old SO answer about Spring: https://stackoverflow.com/a/32073014

This apparently incorrect behaviour of Spring already has a Spring bug report, SPR-10903. The Spring developers closed it as "Works as Designed", noting:

I don't know any rule for comparing media types with their parameters effectively. It really depends on the media type...If you're actually trying to achieve REST versioning through media types, it seems that the most common solution is to use different media types, since their format obviously changed between versions:

comment:3 by Sarah Boyce, 8 weeks ago

Cc: Jake Howard added

comment:4 by Sarah Boyce, 8 weeks ago

Looking at the Accept header in https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2, there are some examples including parameters:

   Media ranges can be overridden by more specific media ranges or
   specific media types.  If more than one media range applies to a
   given type, the most specific reference has precedence.  For example,

     Accept: text/*, text/plain, text/plain;format=flowed, */*

   have the following precedence:

   1.  text/plain;format=flowed

   2.  text/plain

   3.  text/*

   4.  */*

   The media type quality factor associated with a given type is
   determined by finding the media range with the highest precedence
   that matches the type.  For example,

     Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
             text/html;level=2;q=0.4, */*;q=0.5

   would cause the following values to be associated:

   +-------------------+---------------+
   | Media Type        | Quality Value |
   +-------------------+---------------+
   | text/html;level=1 | 1             |
   | text/html         | 0.7           |
   | text/plain        | 0.3           |
   | image/jpeg        | 0.5           |
   | text/html;level=2 | 0.4           |
   | text/html;level=3 | 0.7           |
   +-------------------+---------------+

So I think we probably should take into account parameters but open to hearing other opinions

comment:5 by David Sanders, 8 weeks ago

Nice find 👍 So it sounds like

  • params to be included in matching, but also
  • the precedence is determined by the specificity

? 🤔

comment:6 by Jake Howard, 8 weeks ago

I agree with Sarah that we should take into account the parameters (I'd not come across them before when I implemented this).

The q param specifically will still need some special handling when it comes to ordering, as I think all other parameters should be considered exact matches rather than trying to parse version numbers from them.

I believe the current implementation considers specificity in the ordering, but I may be wrong. Either way, we should make sure it's covered by tests!

comment:7 by Jake Howard, 8 weeks ago

Owner: set to Jake Howard
Status: newassigned
Triage Stage: UnreviewedAccepted

comment:8 by Jake Howard, 8 weeks ago

Has patch: set

comment:9 by Natalia Bidart, 7 weeks ago

Severity: NormalRelease blocker

comment:10 by Natalia Bidart, 7 weeks ago

Summary: get_preferred_type ignores paramsMediaType.get_preferred_type ignores params

in reply to:  description ; comment:11 by Natalia Bidart, 7 weeks ago

Cc: magicfelix added

Replying to magicfelix:

Hello! Could you please check if the proposed PR solves the issue for you?

in reply to:  11 comment:12 by magicfelix, 7 weeks ago

Hello! Could you please check if the proposed PR solves the issue for you?

It does, thanks :)

comment:13 by Natalia Bidart, 7 weeks ago

Patch needs improvement: set

comment:14 by Jake Howard, 7 weeks ago

Patch needs improvement: unset

comment:15 by Natalia Bidart, 6 weeks ago

Triage Stage: AcceptedReady for checkin

comment:16 by nessita <124304+nessita@…>, 6 weeks ago

Resolution: fixed
Status: assignedclosed

In c075508b:

Fixed #36411 -- Made HttpRequest.get_preferred_type() consider media type parameters.

HttpRequest.get_preferred_type() did not account for parameters in
Accept header media types (e.g., "text/vcard; version=3.0"). This caused
incorrect content negotiation when multiple types differed only by
parameters, reducing specificity as per RFC 7231 section 5.3.2
(https://datatracker.ietf.org/doc/html/rfc7231.html#section-5.3.2).

This fix updates get_preferred_type() to treat media types with
parameters as distinct, allowing more precise and standards-compliant
matching.

Thanks to magicfelix for the report, and to David Sanders and Sarah
Boyce for the reviews.

comment:17 by Natalia <124304+nessita@…>, 6 weeks ago

In 0c548e62:

[5.2.x] Fixed #36411 -- Made HttpRequest.get_preferred_type() consider media type parameters.

HttpRequest.get_preferred_type() did not account for parameters in
Accept header media types (e.g., "text/vcard; version=3.0"). This caused
incorrect content negotiation when multiple types differed only by
parameters, reducing specificity as per RFC 7231 section 5.3.2
(https://datatracker.ietf.org/doc/html/rfc7231.html#section-5.3.2).

This fix updates get_preferred_type() to treat media types with
parameters as distinct, allowing more precise and standards-compliant
matching.

Thanks to magicfelix for the report, and to David Sanders and Sarah
Boyce for the reviews.

Backport of c075508b4de8edf9db553b409f8a8ed2f26ecead from main.

Note: See TracTickets for help on using tickets.
Back to Top