Restlet 内容类型协商

Restlet Content Type Negotiation

APIs 支持各种形式的内容协商是很常见的:Accept HTTP header 在请求上,Content-Type HTTP header 在请求上请求(虽然我认为不符合标准),并从文件扩展名中导出预期的类型。我希望我的 Restlet REST API 支持所有这些,我正在寻求帮助以弄清楚如何做到这一点。

我的第一个尝试是查看 Restlet 是否支持开箱即用。我使用的是 Jackson 扩展程序,所以我创建了一个简单的应用程序,它将 /foo 路由到 ServerResource 并返回 Map<String,Boolean>.

当我简单地执行到 /foo 的 GET HTTP URL 连接(la )时,我返回 XML。我希望此默认值为 JSON。那是问题#1。 如何设置默认类型JSON?

当我对 /foo.xml/foo.json 进行相同的 HTTP URL 连接调用时,我收到 404。这是问题 #2。 如何使用文件扩展名来表示预期的媒体类型?

由于我 运行 在 Servlet 容器中,我目前的方法是包装 HttpServletRequest,并将 Accept HTTP header 设置为 application/json 如果没有文件扩展名。这是目前正在解决问题 #1 的技巧。但是,我一直无法以摆脱问题 #2 中的 404 的方式扩展这种方法。

Restlet 使许多其他事情变得简单,以至于我假设我遗漏了一些东西 — 某些配置存在问题,我可以在某处进行调整以使其做正确的事。我在 API 文档中看到了相关提示,但没有什么明显的。那是什么配置呢?

最简单的方法是在应用程序的路由器前添加一个自定义过滤器来执行以下操作:

  • 如果接受的媒体类型列表为空 (request.clientInfo.acceptedMediaTypes),则设置默认媒体类型。 header Accept 的值在此处设置。在执行服务器资源之前设置它很重要,这样可以在从 bean 到表示的转换过程中考虑到这一点。

    Filter preferencesFilter = new Filter(getContext()) {
        protected int beforeHandle(Request request, Response response) {
            if (request.getClientInfo().getAcceptedMediaTypes().isEmpty()) {
                request.getClientInfo().accept(MediaType.APPLICATION_JSON);
            } else if ((request.getClientInfo().getAcceptedMediaTypes().size() == 1)
                && (request.getClientInfo().getAcceptedMediaTypes().get(0).getMetadata().equals(MediaType.ALL))) {
                request.getClientInfo().accept(MediaType.APPLICATION_JSON);
            }
            return super.beforeHandle(request, response);
        }            
    }
    

    供参考,当指定不接受媒体类型时,Restlet 使用已注册的第一个转换器来实际构建响应内容。您的情况似乎是 XML。

  • 检测提供的扩展以根据您的情况推断出相应的可接受媒体类型。 Restlet 的 TunnelService 允许预处理请求以支持使用扩展进行内容协商等功能。您可以简单地按如下所述进行配置:

    public class MyApplication extends Application {
        public MyApplication() {
            getTunnelService().setExtensionsTunnel(true);
        }
    
        @Override
        public Restlet createInboundRoot() {
            (...)
        }
    }
    

否则,您在使用 Restlet 时不应依赖 servlet API。 servlet 扩展只应被视为将 Restlet 应用程序嵌入到 servlet 容器中的适配器...

它在以下地址为您的用例添加了示例项目:https://github.com/templth/restlet-Whosebug/tree/master/restlet/test-restlet-conneg

希望对你有帮助, 蒂埃里