在RESTful HTTP API响应的状态行中本地化原因短语

时间:2011-03-07 09:30:08

标签: http rest localization http-response-codes

我们有一个通过HTTP公开的RESTful API,它以自然的方式利用HTTP状态行(状态代码和原因短语)将API结果传递给客户端(http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html)。

例如,我们有一个并发错误的状态行:

HTTP / 1.1 409资源由其他用户更新。重新加载并重试。

最近发现这些消息将呈现给针对我们的API构建的应用程序的最终用户,这意味着我们需要对它们进行本地化。 我想知道在这种情况下这是否是一种被接受的方法,特别是考虑到这些消息的非ASCII字符集,或者原因短语(状态描述)是否应仅保留为低级别消息和任何内容'应用程序屏幕的方式应该在响应正文中传递?如果我们选择本地化理由短语部分,有什么东西可以在以后咬我们吗?

在这种情况下,我们希望使用响应主体将新版本的资源传递给API客户端,并且包含其他数据并不能很好地发挥其作用。

3 个答案:

答案 0 :(得分:4)

在原因短语中没有达成一致的方式来传递非ASCII字符,所以如果它需要在实验环境之外工作,我会非常小心地依赖它。

将本地化消息移动到响应正文中将更加可靠。

答案 1 :(得分:3)

您将从RFC中了解到:

  

状态代码供...使用   自动机和Reason-Phrase是   供人类用户使用

因此,尽管仍然是用户友好的,但仍然值得尝试在简短和重要之间找到平衡点。我见过的许多网络服务都有一个状态节点(xml或json),其中包含代码和更人性化的消息,作为响应数据的一部分。

一个潜在的问题:在我们的一个RESTful API中,我们定制了原因短语以帮助调试。我们正在使用Pingdom监视正常运行时间,如果状态与RFC中建议的状态相匹配,则错误地仅接受服务可用 - (即200 OK)。在向他们指出这是不正确的之后,我被提到了RFC(!),尽管他们最终承认他们误解了它。

答案 2 :(得分:0)

最佳实践似乎是为此使用ASCII英文消息,并假定浏览器做正确的事情,根本不显示这些短语(请参见下面的详细信息)。

HTTP/1.1 RFC 2616中定义的HTTP 1.1响应行的当前状态以及RFC 72307231中的更新如下:

  • 状态行的格式为HTTP-Version Status-Code Reason-Phrase CRLF
  • Reason-PhraseTEXTCR字符以外的任何LF。 RFC 7230实际上说客户端应该忽略原因短语内容。浏览器不应再向用户显示这些原因短语。它们旨在用于基于文本的浏览器。但是,在现代浏览器仍可能向用户显示原因短语的任何情况下,我都找不到清晰的文档。
  • TEXT规则仅用于描述性字段内容和消息解析器不打算解释的值。仅在根据RFC 2047的规则进行编码时,*TEXT的单词才能包含ISO-8859-1(根据RFC 2616)之外的字符集中的字符。
  • RFC 2047指定语法,例如:=?iso-8859-1?q?test_S=EEne_li=F0e?==?utf-8?b?dGVzdCBTw65uZSBsacOwZSDhmqDhm4fhmrsgz4PPgM6vz4TOuQ==?=。这是其他HTTP标头的编码方式,但当前的浏览器(在Chrome和Firefox上进行了测试)似乎不支持此功能
  • 在Chrome和Firefox上进行的简短测试表明,根据Javascript控制台和“网络”标签中显示的内容,使用响应字段的UTF-8或ISO-8859-1可能会有所不同,但RFC 2047 MIME模式编码永远行不通。

这是我用来测试的一个非常简单的Python 3服务器(例如转到http://127.0.0.1:8080/UTF-8http://127.0.0.1:8080/rfc2047):

# -*- coding: utf-8 -*-
import sys
from email.header import *
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingTCPServer

class TestHTTPRequestHandler(BaseHTTPRequestHandler):
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        print("Received request: %r" % data)
        if 'GET /' in data:
            encoding = data[data.find('GET /')+4:]
            encoding = encoding[:encoding.find(' ')].lstrip('/')
            if '?' in encoding:
                encoding = encoding[:encoding.find('?')]
        else:
            encoding = "iso-8859-1"
        if encoding == 'favicon.ico':
            self.request.sendall(b'HTTP/1.1 404 Not Found\r\n')
            return
        reason_phrase = "test Sîne"
        if encoding == "rfc2047":
            encoding = "utf-8"
            reason_phrase = Header(reason_phrase, encoding).encode()
        response = (u"HTTP/1.1 500 %s\r\nContent-Length: 10\r\nContent-Type: text/plain; charset=%s\r\nConnection: Closed\r\n\r\n\"testSîn\"" % (reason_phrase, encoding))
        self.request.sendall(response.encode(encoding))

server = ThreadingTCPServer(('127.0.0.1', 8080), TestHTTPRequestHandler)
try:
    server.serve_forever()
finally:
    server.server_close()