Stephan Koops | 18 Feb 17:22
Picon

HTML with REST

Hello,

if a browser requests to a REST server, some browsers (Firefox and IE 
for example, Opera not) requests text/xml and application/xml with a 
higher quality than text/html. So, if the server want to allow HTML and 
XML data (e.g. for data about persons) and choose by the quality of the 
accepted Media Types, it returnes ever XML and not HTML to the browser. 
This is not useful. Must I first check, if text/html (or text/xhtml) is 
accepted and ignore the quality? Or should I use the MediaType 
application/person+xml instead of application/xml?

best regards
   Stephan

Rob Heittman | 18 Feb 17:41
Gravatar

Re: HTML with REST


This legacy browser behavior is frustrating in the extreme.  Why in heaven's name would a tool meant primarily for viewing HTML, request XML as a higher quality representation?  Just goes to show how uber-excited everybody was about XML once upon a time.  You know, because in the future, all web pages will someday be XML with a reference to an XSL stylesheet, not HTML.

Choosing a different MediaType for your XML, that the browser doesn't ask for, is the usual solution.

Another workable solution I have found -- if you are using XML and will get criticized for making up MIME types -- is to expose the browser-friendly HTML variant by itself on a distinct URI (e.g. person.html).  That's sloppy too, just in a different way.

Now, packaging your data with JSON instead of XML will avoid the issue altogether, without making up MIME types  =)

- R


On 2/18/08, Stephan Koops <Stephan.Koops <at> web.de> wrote:
if a browser requests to a REST server, some browsers (Firefox and IE
for example, Opera not) requests text/xml and application/xml with a
higher quality than text/html.
keke | 4 Mar 03:00
Picon
Gravatar

Re: HTML with REST

> Another workable solution I have found -- if you are using XML and will get
> criticized for making up MIME types -- is to expose the browser-friendly
> HTML variant by itself on a distinct URI (e.g. person.html).  That's sloppy
> too, just in a different way.

I think this might be a better and clear way than specifying content
type in the HTTP header. The book Restful web service also recommends
doing this. It is both friendly to browser client and programmatic
client.

On Tue, Feb 19, 2008 at 12:41 AM, Rob Heittman
<rob.heittman <at> solertium.com> wrote:
>
> This legacy browser behavior is frustrating in the extreme.  Why in heaven's
> name would a tool meant primarily for viewing HTML, request XML as a higher
> quality representation?  Just goes to show how uber-excited everybody was
> about XML once upon a time.  You know, because in the future, all web pages
> will someday be XML with a reference to an XSL stylesheet, not HTML.
>
> Choosing a different MediaType for your XML, that the browser doesn't ask
> for, is the usual solution.
>

>
> Now, packaging your data with JSON instead of XML will avoid the issue
> altogether, without making up MIME types  =)
>
> - R
>
>
>
>
> On 2/18/08, Stephan Koops <Stephan.Koops <at> web.de> wrote:
> > if a browser requests to a REST server, some browsers (Firefox and IE
> > for example, Opera not) requests text/xml and application/xml with a
> > higher quality than text/html.
>

--

-- 
Cheers,
Keke
-----------------
We paranoid love life

John D. Mitchell | 4 Mar 04:28
Picon

Re: HTML with REST

On Mon, Mar 3, 2008 at 6:00 PM, keke <iamkeke <at> gmail.com> wrote:
[...]
> > Another workable solution I have found -- if you are using XML and will get
>  > criticized for making up MIME types -- is to expose the browser-friendly
>  > HTML variant by itself on a distinct URI (e.g. person.html).  That's sloppy
>  > too, just in a different way.
>
>  I think this might be a better and clear way than specifying content
>  type in the HTTP header. The book Restful web service also recommends
>  doing this. It is both friendly to browser client and programmatic
>  client.

It's easy enough to support both approaches in the Resources' code.
I.e., it doesn't have to be an either/or dichotomy.

Take care,
John

Jerome Louvel | 4 Mar 12:57
Favicon

RE: HTML with REST


Hi all,

I agree that would should support both approaches. 

Actually, there is already the TunnelService that allows you, among other
things, to override the "Accept" header by using a query parameter
(/myResource?media=html). The Directory class also allows the specification
of the mediatype by appending the right 'file' extension.

We could add a feature to this service that would transparently adjust the
preferences of browsers like IE based on the user agent name. The advantage
is that it would be easier to use and wouldn't "pollute" your application
with client-specific concerns. I think that this is more the responsibility
of the component/container.

I have entered a RFE to keep track of this discussion and attached Stefan's
proposed filter:

"Adjust browser preferences for HTML/XML"
http://restlet.tigris.org/issues/show_bug.cgi?id=455

Best regards,
Jerome  


> -----Message d'origine----- > De : John D. Mitchell [mailto:jdmitchell <at> gmail.com] > Envoyé : mardi 4 mars 2008 04:29 > À : discuss <at> restlet.tigris.org > Objet : Re: HTML with REST > > On Mon, Mar 3, 2008 at 6:00 PM, keke <iamkeke <at> gmail.com> wrote: > [...] > > > Another workable solution I have found -- if you are > using XML and will get > > > criticized for making up MIME types -- is to expose the > browser-friendly > > > HTML variant by itself on a distinct URI (e.g. > person.html). That's sloppy > > > too, just in a different way. > > > > I think this might be a better and clear way than > specifying content > > type in the HTTP header. The book Restful web service also > recommends > > doing this. It is both friendly to browser client and programmatic > > client. > > It's easy enough to support both approaches in the Resources' code. > I.e., it doesn't have to be an either/or dichotomy. > > Take care, > John
Stephan Koops | 3 Mar 22:25
Picon

Re: HTML with REST

Hello,

as discussed two weeks ago, some browsers (e.g. Internet Explorer 7.0 and Firefox 2.0) sends as accepted media type XML with a higher quality than HTML. The consequence is, that a HTTP server sends XML instead of HTML, if it could produce XML.
For this problem I've created a Filter (named HtmlPreferer now), that will increase the qualities for HTML media types (text/html and application/xhtml+xml) higher than both XML types (text/xml and application/xml), if at least one of both is available in a request.

Now it is available in the JAX-RS extension. What do you think about make it available for all developers in the main project? I put the actual source and a test case as attachment.

Jerome or Thierry: If you want to integrate the class in the main package or where ever, use the file of the JAX-RS; perhaps I continue developing or something like this.

best regards
   Stephan

Rob Heittman schrieb:
This legacy browser behavior is frustrating in the extreme.  Why in heaven's name would a tool meant primarily for viewing HTML, request XML as a higher quality representation?  Just goes to show how uber-excited everybody was about XML once upon a time.  You know, because in the future, all web pages will someday be XML with a reference to an XSL stylesheet, not HTML.

Choosing a different MediaType for your XML, that the browser doesn't ask for, is the usual solution.

Another workable solution I have found -- if you are using XML and will get criticized for making up MIME types -- is to expose the browser-friendly HTML variant by itself on a distinct URI (e.g. person.html).  That's sloppy too, just in a different way.

Now, packaging your data with JSON instead of XML will avoid the issue altogether, without making up MIME types  =)

- R

On 2/18/08, Stephan Koops <Stephan.Koops <at> web.de> wrote:
if a browser requests to a REST server, some browsers (Firefox and IE
for example, Opera not) requests text/xml and application/xml with a
higher quality than text/html.
/*
 * Copyright 2005-2008 Noelios Consulting.
 * 
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the "License"). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL HEADER in each file and
 * include the License file at http://www.opensource.org/licenses/cddl1.txt If
 * applicable, add the following below this CDDL HEADER, with the fields
 * enclosed by brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 */
package org.restlet.ext.jaxrs;

import java.util.List;
import java.util.Map;

import org.restlet.Context;
import org.restlet.Filter;
import org.restlet.Restlet;
import org.restlet.data.MediaType;
import org.restlet.data.Preference;
import org.restlet.data.Request;
import org.restlet.data.Response;

/**
 * <p>
 * Some browsers (e.g. Internet Explorer 7.0 and Firefox 2.0) sends as accepted
 * media type XML with a higher quality than html. The consequence is, that a
 * HTTP server sends XML instead of HTML, if it could produce XML. To avoid
 * this, you can use this filter.
 * </p>
 * <p>
 * This Filter will increase the qualities for HTML media types (text/html and
 * application/xhtml+xml) higher than both XML types (text/xml and
 * application/xml), if at least one of both is available in a request. The
 * check is implemented in method {@link #shouldChangeToPrefereHtml(Request)}.
 * <br>
 * Requests that not are not effected.
 * </p>
 * <p>
 * You may alter the test if the filter should change the request by subclass
 * this Filter and overrider method {@link #shouldChangeToPrefereHtml(Request)}.
 * </p>
 * 
 * @author Stephan Koops
 */
public class HtmlPreferer extends Filter {

    private static final int MT_PREF_APP_XHTML = 1;

    private static final int MT_PREF_APP_XML = 3;

    private static final int MT_PREF_TEXT_HTML = 0;

    private static final int MT_PREF_TEXT_XML = 2;

    private static final String MT_QUALITY_ARRAY = "org.restlet.HtmlPreferer.qualities";

    /**
     * Creates a new {@link HtmlPreferer}. You should use constructor
     * {@link #HtmlPreferer(Context)} or {@link #HtmlPreferer(Context, Restlet)}.
     */
    @Deprecated
    public HtmlPreferer() {
        super();
    }

    /**
     * Creates a new {@link HtmlPreferer}. You can give also the next restlet
     * by using constructor {@link #HtmlPreferer(Context, Restlet)}.
     * 
     * @param context
     *                the context from the parent
     */
    public HtmlPreferer(Context context) {
        super(context);
    }

    /**
     * Creates a new {@link HtmlPreferer}.
     * 
     * @param context
     *                the context from the parent
     * @param next
     *                the {@link Restlet} to call after filtering.
     */
    public HtmlPreferer(Context context, Restlet next) {
        super(context, next);
    }

    /**
     * Allows filtering before processing by the next Restlet.
     * 
     * @param request
     *                The request to filter.
     * @param response
     *                The response to update.
     * @return The continuation status, see
     *         {@link Filter#beforeHandle(Request, Response)}
     * @see Filter#beforeHandle(Request, Response)
     */
    @Override
    protected int beforeHandle(Request request, Response response) {
        if (shouldChangeToPrefereHtml(request))
            prefereHtml(request);
        return super.beforeHandle(request, response);
    }

    /**
     * Returns the quality of accepted media type application/xhtml+xml, or
     * null, if not present in the given request.
     * 
     * @param request
     * @return the quality of accepted media type application/xhtml+xml, or
     *         null, if not present in the given request.
     * @see #getHtmlMinQuality(Request)
     */
    protected Float getAppXhtmlQuality(Request request) {
        return getHtmlXmlMtQualities(request)[MT_PREF_APP_XHTML];
    }

    /**
     * Returns the quality of accepted media type app/xml, or null, if not
     * present in the given request.
     * 
     * @param request
     * @return the quality of accepted media type app/xml, or null, if not
     *         present in the given request.
     * @see #getXmlMaxQuality(Request)
     */
    protected Float getAppXmlQuality(Request request) {
        return getHtmlXmlMtQualities(request)[MT_PREF_APP_XML];
    }

    /**
     * Returns the lowest quality of the HTML types (text/html and
     * application/xhtml+xml), or null, if not available.
     * 
     * @param request
     * @return the lowest quality of the HTML types (text/html and
     *         application/xhtml+xml), or null, if not available.
     * @see #getTextHtmlQuality(Request)
     * @see #getAppXhtmlQuality(Request)
     */
    protected Float getHtmlMinQuality(Request request) {
        Float xhtmlQuality = getAppXhtmlQuality(request);
        Float htmlQuality = getTextHtmlQuality(request);
        if (xhtmlQuality == null)
            return htmlQuality;
        if (htmlQuality == null)
            return xhtmlQuality;
        return Math.min(xhtmlQuality, htmlQuality);
    }

    @SuppressWarnings("unchecked")
    private Float[] getHtmlXmlMtQualities(Request request) {
        Float[] htmlXmlQualities;
        Map<String, Object> attributes = request.getAttributes();
        htmlXmlQualities = (Float[]) attributes.get(MT_QUALITY_ARRAY);
        if (htmlXmlQualities == null) {
            htmlXmlQualities = new Float[4];
            List<Preference<MediaType>> acceptedMediaTypes = request
                    .getClientInfo().getAcceptedMediaTypes();
            for (Preference<MediaType> accPref : acceptedMediaTypes) {
                MediaType accMediaType = accPref.getMetadata();
                if (accMediaType.equals(MediaType.TEXT_HTML, true))
                    htmlXmlQualities[MT_PREF_TEXT_HTML] = accPref.getQuality();
                if (accMediaType.equals(MediaType.TEXT_XML, true))
                    htmlXmlQualities[MT_PREF_TEXT_XML] = accPref.getQuality();
                if (accMediaType.equals(MediaType.APPLICATION_XHTML_XML, true))
                    htmlXmlQualities[MT_PREF_APP_XHTML] = accPref.getQuality();
                if (accMediaType.equals(MediaType.APPLICATION_XML, true))
                    htmlXmlQualities[MT_PREF_APP_XML] = accPref.getQuality();
            }
            attributes.put(MT_QUALITY_ARRAY, htmlXmlQualities);
        }
        return htmlXmlQualities;
    }

    /**
     * Returns the quality of accepted media type text/html, or null, if not
     * present in the given request.
     * 
     * @param request
     * @return the quality of accepted media type text/html, or null, if not
     *         present in the given request.
     * @see #getHtmlMinQuality(Request)
     */
    protected Float getTextHtmlQuality(Request request) {
        return getHtmlXmlMtQualities(request)[MT_PREF_TEXT_HTML];
    }

    /**
     * Returns the quality of accepted media type text/xml, or null, if not
     * present in the given request.
     * 
     * @param request
     * @return the quality of accepted media type text/xml, or null, if not
     *         present in the given request.
     * @see #getXmlMaxQuality(Request)
     */
    protected Float getTextXmlQuality(Request request) {
        return getHtmlXmlMtQualities(request)[MT_PREF_TEXT_XML];
    }

    /**
     * Returns the highest quality of the XML types (text/xml and
     * application/xml), or null, if not available.
     * 
     * @param request
     * @return the highest quality of the XML types (text/xml and
     *         application/xml), or null, if not available.
     * @see #getAppXmlQuality(Request)
     * @see #getTextXmlQuality(Request)
     */
    protected Float getXmlMaxQuality(Request request) {
        Float appXmlQuality = getAppXmlQuality(request);
        Float textXmlQuality = getTextXmlQuality(request);
        if (appXmlQuality == null)
            return textXmlQuality;
        if (textXmlQuality == null)
            return appXmlQuality;
        return Math.max(appXmlQuality, textXmlQuality);
    }

    /**
     * Alters the request, that HTML is prefered before XML.
     * 
     * @param request
     *                the request to alter.
     */
    protected void prefereHtml(Request request) {
        Float xmlQualityO = getXmlMaxQuality(request);
        if (xmlQualityO == null)
            return;
        float xmlQuality = xmlQualityO;
        float htmlMinQuality;
        if (xmlQuality < 1) {
            htmlMinQuality = xmlQuality + 0.001f;
        } else {
            lowerWithQuality(request, xmlQuality);
            htmlMinQuality = 1;
        }
        htmlPrefsMin(request, htmlMinQuality);
    }

    /**
     * Lowers all accepted media type Preferences with the given quality to
     * 0.001 (littlest accuracy for HTML qualities). Also lowers recursive the
     * preferences with the goal quality.
     * 
     * @param request
     * @param quality
     */
    private void lowerWithQuality(Request request, float quality) {
        List<Preference<MediaType>> acceptedMediaTypes = request
                .getClientInfo().getAcceptedMediaTypes();
        float goalQuality = quality - 0.001f;
        boolean alreadyAvailable = false;
        for (Preference<MediaType> accPref : acceptedMediaTypes) {
            if (accPref.getQuality() == goalQuality) {
                alreadyAvailable = true;
                break;
            }
        }
        if (alreadyAvailable)
            lowerWithQuality(request, goalQuality);
        for (Preference<MediaType> accPref : acceptedMediaTypes) {
            if (accPref.getQuality() == quality)
                accPref.setQuality(goalQuality);
        }
    }

    /**
     * sets the quality of the preferences of the HTML media types (text/html
     * and app/xhtml) at least to the given quality.
     * 
     * @param request
     * @param htmlQuality
     */
    private void htmlPrefsMin(Request request, float htmlQuality) {
        for (Preference<MediaType> accPref : request.getClientInfo()
                .getAcceptedMediaTypes()) {
            MediaType accMediaType = accPref.getMetadata();
            if (accMediaType.equals(MediaType.APPLICATION_XHTML_XML, true)
                    || accMediaType.equals(MediaType.TEXT_HTML, true)) {
                if (accPref.getQuality() < htmlQuality)
                    accPref.setQuality(htmlQuality);
            }
        }
    }

    /**
     * This method checks, if HTML should be prefered for the given request. The
     * check may be overridden or complemented by override that method.
     * 
     * @param request
     *                the request to check.
     * @return true, if the {@link Request} should be altered, or false if not.
     */
    protected boolean shouldChangeToPrefereHtml(Request request) {
        Float htmlQuality = getHtmlMinQuality(request);
        Float xmlQuality = getXmlMaxQuality(request);
        if (htmlQuality == null || xmlQuality == null)
            return false;
        return xmlQuality >= htmlQuality;
    }
}
/*
 * Copyright 2005-2008 Noelios Consulting.
 * 
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the "License"). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL HEADER in each file and
 * include the License file at http://www.opensource.org/licenses/cddl1.txt If
 * applicable, add the following below this CDDL HEADER, with the fields
 * enclosed by brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 */
package org.restlet.test.jaxrs;

import java.util.List;

import org.restlet.Restlet;
import org.restlet.data.MediaType;
import org.restlet.data.Preference;
import org.restlet.data.Request;
import org.restlet.ext.jaxrs.HtmlPreferer;

import junit.framework.TestCase;

/**
 * This TextCase checks the {@link HtmlPreferer}.
 * 
 * @author Stephan Koops
 */
public class HtmlPrefererTest extends TestCase {

    private static final HtmlPreferer HTML_PREFERER = new HtmlPreferer(null,
            new Restlet());

    /**
     * @param accMediaTypes
     * @param mediaType
     * @param quality
     */
    private void addMediaTypePref(List<Preference<MediaType>> accMediaTypes,
            MediaType mediaType, float quality) {
        accMediaTypes.add(new Preference<MediaType>(mediaType, quality));
    }

    /**
     * @param accMediaTypes
     * @param q0
     * @param q1
     * @param q2
     * @param q3
     * @param q4
     */
    private void check(List<Preference<MediaType>> accMediaTypes, float q0,
            float q1, float q2, float q3, float q4) {
        assertEquals(5, accMediaTypes.size());
        Preference<MediaType> amt0 = accMediaTypes.get(0);
        Preference<MediaType> amt1 = accMediaTypes.get(1);
        Preference<MediaType> amt2 = accMediaTypes.get(2);
        Preference<MediaType> amt3 = accMediaTypes.get(3);
        Preference<MediaType> amt4 = accMediaTypes.get(4);
        assertEquals(q0, amt0.getQuality());
        assertEquals(q1, amt1.getQuality());
        assertEquals(q2, amt2.getQuality());
        assertEquals(q3, amt3.getQuality());
        assertEquals(q4, amt4.getQuality());
    }

    public void test1() {
        Request request = new Request();
        List<Preference<MediaType>> accMediaTypes = request.getClientInfo()
                .getAcceptedMediaTypes();
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_ALL, 0.2f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_HTML, 1f);
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_XHTML_XML, 0.9f);
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_XML, 0.8f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_XML, 0.7f);
        HTML_PREFERER.handle(request);
        check(accMediaTypes, 0.2f, 1f, 0.9f, 0.8f, 0.7f);
    }

    public void test2() {
        Request request = new Request();
        List<Preference<MediaType>> accMediaTypes = request.getClientInfo()
                .getAcceptedMediaTypes();
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_ALL, 0.2f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_HTML, 0.6f);
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_XHTML_XML, 0.9f);
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_XML, 0.8f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_XML, 0.7f);
        HTML_PREFERER.handle(request);
        check(accMediaTypes, 0.2f, 0.801f, 0.9f, 0.8f, 0.7f);
    }

    public void test3() {
        Request request = new Request();
        List<Preference<MediaType>> accMediaTypes = request.getClientInfo()
                .getAcceptedMediaTypes();
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_ALL, 0.2f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_HTML, 0.6f);
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_XHTML_XML, 1f);
        addMediaTypePref(accMediaTypes, MediaType.IMAGE_BMP, 0.8f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_PLAIN, 0.7f);
        HTML_PREFERER.handle(request);
        check(accMediaTypes, 0.2f, 0.6f, 1f, 0.8f, 0.7f);
    }

    public void test4() {
        Request request = new Request();
        List<Preference<MediaType>> accMediaTypes = request.getClientInfo()
                .getAcceptedMediaTypes();
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_ALL, 0.2f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_HTML, 0.6f);
        addMediaTypePref(accMediaTypes, MediaType.APPLICATION_XML, 1f);
        addMediaTypePref(accMediaTypes, MediaType.IMAGE_BMP, 0.999f);
        addMediaTypePref(accMediaTypes, MediaType.TEXT_PLAIN, 0.7f);
        HTML_PREFERER.handle(request);
        check(accMediaTypes, 0.2f, 1f, 0.999f, 0.998f, 0.7f);
    }
}
Thierry Boileau | 18 Feb 19:39
Picon

Re: HTML with REST

Hello all,

you may also add a new Filter in front of your application which
checks the agent (=>request.getClientInfo().getAgent()) and update the
quality of some accepted media-types (=>
request.getClientInfo().getAcceptedMediaTypes());
Or add a new preference for media-type "text/html" at the beginning of
the list with a quality of 1.

best regards,
Thierry Boileau

On Feb 18, 2008 5:41 PM, Rob Heittman <rob.heittman <at> solertium.com> wrote:

> > This legacy browser behavior is frustrating in the extreme. Why in heaven's > name would a tool meant primarily for viewing HTML, request XML as a higher > quality representation? Just goes to show how uber-excited everybody was > about XML once upon a time. You know, because in the future, all web pages > will someday be XML with a reference to an XSL stylesheet, not HTML. > > Choosing a different MediaType for your XML, that the browser doesn't ask > for, is the usual solution. > > Another workable solution I have found -- if you are using XML and will get > criticized for making up MIME types -- is to expose the browser-friendly > HTML variant by itself on a distinct URI (e.g. person.html). That's sloppy > too, just in a different way. > > Now, packaging your data with JSON instead of XML will avoid the issue > altogether, without making up MIME types =) > > - R > > > > > On 2/18/08, Stephan Koops <Stephan.Koops <at> web.de> wrote: > > if a browser requests to a REST server, some browsers (Firefox and IE > > for example, Opera not) requests text/xml and application/xml with a > > higher quality than text/html. >

Gmane