////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////

Grails has built in support for https://en.wikipedia.org/wiki/Content_negotiation[Content negotiation] using either the HTTP `Accept` header, an explicit format request parameter or the extension of a mapped URI.


==== Configuring Mime Types


Content negotiation works against a set of supported content types. As of Grails 8 these are provided by the framework as built-in defaults, so a newly generated application does **not** need to declare them in `grails-app/conf/application.yml`. The built-in defaults are equivalent to the following `grails.mime.types` setting:

[source,yaml]
----
grails:
    mime:
        types:
            all: '*/*'  # 'all' maps to '*' or the first available format in withFormat
            atom: application/atom+xml
            css: text/css
            csv: text/csv
            form: application/x-www-form-urlencoded
            html:
              - text/html
              - application/xhtml+xml
            js: text/javascript
            json:
              - application/json
              - text/json
            multipartForm: multipart/form-data
            pdf: application/pdf
            rss: application/rss+xml
            text: text/plain
            hal:
              - application/hal+json
              - application/hal+xml
            xml:
              - text/xml
              - application/xml
----

This is what allows Grails to detect the format of a request containing either the 'text/xml' or 'application/xml' media types as simply 'xml'. The first entry is the default format.

You only need to add the `grails.mime.types` setting when you want a different set of content types from the defaults. As in earlier Grails versions, a declared `grails.mime.types` **replaces** the built-in defaults, so the map you declare is the complete, authoritative set (and the first entry is the default format). This is also how you restrict the negotiable types — declare only the ones you want and the rest are not recognised:

[source,yaml]
----
grails:
    mime:
        types:
            json:
              - application/json
              - text/json
            xml:
              - text/xml
              - application/xml
----

If instead you want to **add to** the built-in defaults without re-declaring them, enable merging with `grails.mime.mergeDefaults`. Your declared entries are then merged over the defaults: an entry replaces the default for the matching extension, any extension you do not declare keeps its default, and your declared entries are ordered first (so your first declared type remains the default format):

[source,yaml]
----
grails:
    mime:
        mergeDefaults: true
        types:
            custom: application/vnd.example+json
----

NOTE: To restrict what an individual endpoint serves (rather than the application-wide set of recognised types), use the `responseFormats` property on a controller or the `formats:` argument of `respond`, which is the recommended, scoped way to limit content negotiation for a given action.


==== Content Negotiation using the format Request Parameter


Let's say a controller action can return a resource in a variety of formats: HTML, XML, and JSON. What format will the client get? The easiest and most reliable way for the client to control this is through a `format` URL parameter.

So if you, as a browser or some other client, want a resource as XML, you can use a URL like this:

[source]
----
https://my.domain.org/books.xml
----

NOTE: The request parameters `format` is allowed as well `https://my.domain.org/books?format=xml`, but the default Grails URL Mapping `get "/$controller(.$format)?"(action:"index")` will override the `format` parameter with null. So the default mapping should be updated to `get "/$controller"(action:"index")`.

The result of this on the server side is a `format` property on the `response` object with the value `xml` .

You can also define this parameter in the link:theWebLayer.html#urlmappings[URL Mappings] definition:

[source,groovy]
----
"/book/list"(controller:"book", action:"list") {
    format = "xml"
}
----

You could code your controller action to return XML based on this property, but you can also make use of the controller-specific `withFormat()` method:

NOTE: This example requires the addition of the `org.apache.grails:grails-converters` plugin

[source,groovy]
----
import grails.converters.JSON
import grails.converters.XML

class BookController {

    def list() {
        def books = Book.list()

        withFormat {
            html bookList: books
            json { render books as JSON }
            xml { render books as XML }
            '*' { render books as JSON }
        }
    }
}
----

In this example, Grails will only execute the block inside `withFormat()` that matches the requested content type. So if the preferred format is `html` then Grails will execute the `html()` call only. Each 'block' can either be a map model for the corresponding view (as we are doing for 'html' in the above example) or a closure. The closure can contain any standard action code, for example it can return a model or render content directly.

When no format matches explicitly, a `*` (wildcard) block can be used to handle all other formats.

There is a special format, "all", that is handled differently from the explicit formats. If "all" is specified (normally this happens through the Accept header - see below), then the first block of `withFormat()` is executed when there isn't a `*` (wildcard) block available.

You should not add an explicit "all" block. In this example, a format of "all" will trigger the `html` handler (`html` is the first block and there is no `*` block).

[source,groovy]
----
withFormat {
    html bookList: books
    json { render books as JSON }
    xml { render books as XML }
}
----

NOTE: When using link:{controllersRef}withFormat.html[withFormat] make sure it is the last call in your controller action as the return value of the `withFormat` method is used by the action to dictate what happens next.


==== Using the Accept header


Every incoming HTTP request has a special https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html[Accept] header that defines what media types (or mime types) a client can "accept". A non-browser client such as a JavaScript `fetch` call or a REST client is typically specific about what it wants:

[source,groovy]
----
application/json
----

and Grails honors that: it sets `format` to `json` as you'd expect, and this works with the `withFormat()` method in just the same way as when the `format` URL parameter is set (although the URL parameter takes precedence).

By default Grails honors the `Accept` header for **every** client, including browsers. Modern browsers send a well-formed header that already prioritizes HTML, for example:

[source]
----
text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
----

Here `text/html` has the highest quality (`q=1.0` implicitly) and `application/xml` is explicitly lower at `q=0.9`, so content negotiation correctly resolves a browser navigation to HTML without any special handling.

[NOTE]
====
This is a change from Grails 7 and earlier, which ignored the `Accept` header for browser user agents by default. That behavior was a workaround for very old browsers (such as Firefox 2 and 3) that sent headers like `text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,...`, ranking XML above HTML. Modern browsers no longer do this, and ignoring the header prevented browser-issued `fetch`/`XMLHttpRequest` calls that ask for `application/json` from receiving JSON.
====

If you need the legacy behavior — ignoring the `Accept` header for specific user agents — configure `grails.mime.disable.accept.header.userAgents` with the user-agent tokens to match (it is unset by default):

[source,groovy]
----
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
----

Or the equivalent in `application.yml`:

[source,yaml]
----
grails:
    mime:
        disable:
            accept:
                header:
                    userAgents: ['Gecko', 'WebKit', 'Presto', 'Trident']
----

When this setting is active, `XMLHttpRequest`-style requests (those sending an `X-Requested-With: XMLHttpRequest` header) still honor the `Accept` header; set `grails.mime.disable.accept.header.userAgentsXhr` to `true` to subject those requests to the user-agent filter as well.

An accept header of '\*/\*' results in a value of `all` for the `format` property.

NOTE: If the accept header is used but contains no registered content types, Grails will assume a broken browser is making the request and will set the HTML format - note that this is different from how the other content negotiation modes work as those would activate the "all" format!



==== Request format vs. Response format


As of Grails 2.0, there is a separate notion of the _request_ format and the _response_ format. The request format is dictated by the `CONTENT_TYPE` header and is typically used to detect if the incoming request can be parsed into XML or JSON, whilst the response format uses the file extension, format parameter or ACCEPT header to attempt to deliver an appropriate response to the client.

The link:{controllersRef}withFormat.html[withFormat] available on controllers deals specifically with the response format. If you wish to add logic that deals with the request format then you can do so using a separate `withFormat` method available on the request:

[source,groovy]
----
request.withFormat {
    xml {
        // read XML
    }
    json {
        // read JSON
    }
}
----


==== Content Negotiation with URI Extensions


Grails also supports content negotiation using URI extensions. For example given the following URI:

[source]
----
/book/list.xml
----

This works as a result of the default URL Mapping definition which is:

[source,groovy]
----
"/$controller/$action?/$id?(.$format)?"{
----

Note the inclusion of the `format` variable in the path. If you do not wish to use content negotiation via the file extension then simply remove this part of the URL mapping:

[source,groovy]
----
"/$controller/$action?/$id?"{
----



==== Testing Content Negotiation


To test content negotiation in a unit or integration test (see the section on link:testing.html[Testing]) you can either manipulate the incoming request headers:

[source,groovy]
----
void testJavascriptOutput() {
    def controller = new TestController()
    controller.request.addHeader "Accept",
              "text/javascript, text/html, application/xml, text/xml, */*"

    controller.testAction()
    assertEquals "alert('hello')", controller.response.contentAsString
}
----

Or you can set the format parameter to achieve a similar effect:

[source,groovy]
----
void testJavascriptOutput() {
    def controller = new TestController()
    controller.params.format = 'js'

    controller.testAction()
    assertEquals "alert('hello')", controller.response.contentAsString
}
----
