Skip to content
This repository

Basic Content Negotiation #329

Open
mvriel opened this Issue · 10 comments

5 participants

Mike van Riel Lukas Kahwe Smith Alex Knol Boris Guéry Valentin Ferriere
Mike van Riel

Dear maintainers,

For a Restful API that we are working on we would like to implement a basic form of content negotiation using the mimetype. What we would want is the version for JmsSerializer and the Format for JmsSerializer is read from the mimetype.

Example:

application/vnd.vendorname.api.user+xml;version=1.0

I have figured out how Symfony and FosRestBundle acknowledge the mime type using the mime-type-listener and format_listener in the app/config/config.yml:

    format_listener:
        default_priorities: ['user', 'xml']
        fallback_format:    'xml'
    view:
        mime_types:
            user: ['application/vnd.vendorname.api.user', 'application/vnd.vendorname.api.user+xml']

But what happens now is that the 'user' identifier is passed to JmsSerializer in the Viewhandler's createResponse method (https://github.com/FriendsOfSymfony/FOSRestBundle/blob/master/View/ViewHandler.php#L332) which it does not recognize.

Since I just want to use the XML serialization I would love to be able to have JmsSerializer just use the XML format. Unfortunately it seems that the createResponse method not only serializes the data using the discovered id but also sets the content type using that same id. This means I cannot separate the serialization format from the content type.

I have also tried to add a custom Serialization and Deserialization service using the XML Serializer and Deserializer but this poses a lot of other issues (schmittjoh/JMSSerializerBundle#241) where Johannes adviced me to just use the XML serializer instead of trying to add my own.

In short, my question is:

What would be the best way to separate serialization from content-type discovery so that I can serialize using XML but have all the other handling of createResponse use application/vnd.vendorname.api.user+xml

I don't mind to write a PR, but I would like to know your opinion on the above

Lukas Kahwe Smith
Owner

maybe we should have a ViewHandler::getContent() method? alternatively/additionally maybe we should have a way to map "abstract formats" to "primitive formats".

Lukas Kahwe Smith
Owner

@Elexy could you look into this topic?

Alex Knol
Elexy commented

Sorry not until next week

Boris Guéry

Since I just want to use the XML serialization I would love to be able to have JmsSerializer just use the XML format. Unfortunately it seems that the createResponse method not only serializes the data using the discovered id but also sets the content type using that same id. This means I cannot separate the serialization format from the content type.

Why would you want to do that? If a client request Accept: application/vnd.vendorname.api.user+xml it should except to get Content-Type: application/vnd.vendorname.api.user+xml it seems logical.

application/vnd.vendorname.api.user+xml is different from application/xml and so on.

Imho, it is totally legitimate to use the same format.


However, you raised a very valid point, the custom media type are extensions of most existing mime type, therefore as it has been advised by @schmittjoh, it is easier to "alias" your media type to a known format.

But currently I don't see any way to this with the current state of FOSRestBundle.

I've exact same requirement so I'm probably gonna work on this.

I quickly dived in the format handling process from setting a new Mime Type with the MimeTypeListener through the Request to the ViewHandler.

At a quick glance, I think of some Mapper to map registered Mime Type to Serialization format, it would be done without any BC breaks which seems cool.

An example of configuration could be:

serialization_format_mapper:
  application/vnd.vendorname.api.user+xml:  xml
  application/vnd.vendorname.api.user+json: json
  application/vnd.vendorname.api.user:      xml   #fallback

The createResponse() method could be modified like:

if ($this->isFormatTemplating($format)) {
     $content = $this->renderTemplate($view, $format);
} else {
    $serializer = $this->getSerializer($view); 
    $serializationFormat = $format;
    if (isset($this->serializationFormatsMap[$request->getMimeType($format)])) {
        $serializationFormat = $this->serializationFormatsMap[$request->getMimeType($format)];
    }

    $content = $serializer->serialize($view->getData(), $serializationFormat);
}

Any thoughts?

Boris Guéry

I feel stupid, thank you @lsmith77.

Boris Guéry

mmm well I just tested this configuration, but it doesn't behave as I expected to.

> Accept: application/vnd.acme+json
> Accept-Encoding: gzip, deflate, compress
> User-Agent: HTTPie/0.3.1
< HTTP/1.1 200 OK
< Content-Encoding: gzip
< Content-Type: application/json

The Content-Type should be the same as the Accept header request based on priority.

It is incorrect to serve application/json when the client requires application/vnd.acme+json.

Lukas Kahwe Smith
Owner
Boris Guéry

I'm ok for the option, it'll make all people happy I guess, I'll try to write a PR for this

Boris Guéry borisguery referenced this issue from a commit in borisguery/FOSRestBundle
Boris Guéry refs #329 - Allow to map custom mime type and their respective
identifier to a serialization format

Example:

```yaml
mime_type:
    acme_user_json: ['application/vnd.acme.user+json']

serialization_formats:
    acme_user_json: json # this is the format configured within JMS Serializer
```

If no serialization format are defined for a given mime type, the
format is passed as is.
145a47f
Valentin Ferriere

:+1: for this commit :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.