Including A DOCTYPE Declaration With A Servlet Response
You want to include a DOCTYPE declaration in your servlet response, whilst retaining the ease-of-use of the X-expression format.
The following function takes an X-expression and returns a servlet response that contains the response and the XHTML transitional DOCTYPE.
(define (make-xhtml-response xexpr)
(list "text/html"
"<!DOCTYPE html
PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
(xexpr->string xexpr)))
A servlet can return three different types of responses:
- An X-expression, the most convenient type of response to use.
- A list of strings. The first string is the MIME type, the rest the content.
- A response constructed using
make-response/full
The code above uses the second method to prepend the XHTML Transitional DOCTYPE declaration to an X-expression response. The response is given the text/html MIME type. This is not recommended by the W3C, but Internet Explorer is not standards compliant and fails to correctly process file with the recommended MIME type.
The documentation for X-expressions says they represent the "interesting" part of an XML document. This is true if you aren't interested in XML standards, but the increasing use of XML on the web means that applications have to be aware of the additional requirements of XML. In particular Internet Explorer 6 reverts to the broken Internet Explorer 5 rendering model if HTML documents don't have a DOCTYPE (
FireFox? of course does the right thing).
The XML library can represent a DOCTYPE, though the X-expression format cannot. To add a DOCTYPE to an X-expression by converting it to the structure representation defined in the XML library you could use the following function:
(define (xexpr->xhtml xexpr)
(make-document
(make-prolog '()
(make-document-type
'html
(make-external-dtd/public
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
"-//W3C//DTD XHTML 1.0 Transitional//EN")
#f))
(xexpr->xml xexpr)))
The thing is, if you're going to use XHTML, you're probably going to need fine control over the HTTP headers as well. According to the W3C, there are strict requirements about the MIME type reported by the web browser for XHTML. The proper MIME type is
application/xhtml+xml, but unfortunately not all browsers support this MIME type. The solution is to take a look at the
"Accept" header of the request and see if the browser supports it, and otherwise use
text/html.
(require (lib "servlet.ss" "web-server")
(lib "response.ss" "web-server")
(lib "etc.ss")
(lib "string.ss")
(lib "xml.ss" "xml"))
(define (accepted-mime-types request)
(with-handlers ([exn? (lambda (exn) null)])
(let ([header (extract-binding/single 'accept (request-headers request))])
(regexp-split #rx",[ \t\r\n]*"
(if (bytes? header)
(bytes->string/latin-1 header)
header)))))
(define (xhtml-mime-type request)
(if (and request (member "application/xhtml+xml" (accepted-mime-types request)))
#"application/xhtml+xml"
#"text/html"))
(define doctype
(string-append "<!DOCTYPE html "
"PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"))
(define make-response/xhtml
(opt-lambda (xexpr [request #f])
(make-response/full
200
"Okay"
(current-seconds)
(xhtml-mime-type request)
null
(list doctype (xexpr->string xexpr)))))
I should probably make this into a
PLaneT package.
See also:
--
DaveHerman - 30 Sep 2005
Okay, I've added a new
PLaneT package for this. Use:
(require (planet "xhtml.ss" ("dherman" "xhtml.plt" 1 1)))
--
DaveHerman - 30 Sep 2005
--
NoelWelsh - 22 Nov 2004