覆盖REST API中的JSON编码

时间:2017-07-05 作者:Jumi

我正在尝试创建一个安全的下载插件。

我发现rest api是这个任务的最佳选择,但我不能像这样更改标题Content-Type:. 在register_rest_route 回调已设置的这些标头。如果我设置Content-disposition: attachment, filename=asd.txt 我明白了ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION 镀铬。

我知道rest不能处理这些任务,但我找不到任何替代方案。如果有,请告诉我。

下载的数据似乎是json编码的。

我讨厌wordpress的“魔法”。使用普通PHP可以很容易地完成这些任务,但wordpress只会让它变得复杂。

请有人告诉我如何禁用这些魔法头***或推荐我一种在插件中安全下载(php)的方法。

(不,我不想使用第三方插件。我已经尝试了很多插件。到目前为止,它们都没有起作用……:D这些wordpress的事情在symfony之后是一个巨大的倒退……)

这是我的experimental 代码:

add_action(\'rest_api_init\', function() {
    register_rest_route(\'secure-downloads/v1\', \'/download/(?P<filename>.*)\', 
    array(
        \'methods\' => \'GET\',
        \'callback\' => \'secure_downloads_handle_download\'
    ));
});
function secure_downloads_handle_download(WP_REST_Request $request)
{
    $filename = urldecode($request->offsetGet(\'filename\'));
    if(strpos($filename, \'/..\'))
    {
        return new WP_REST_Response(\'\', 403);
    }
    $path = SECURE_DOWNLOADS_UPLOAD_DIR . $filename;
    return new WP_REST_Response(file_get_contents($path), 200, array(
        \'Content-Type\' => \'application/octet-stream\',
        \'Content-Transfer-Encoding\' => \'Binary\',
        \'Content-disposition\' => \'attachment, filename=\\\\\' . $filename . \'\\\\\'
    ));
}

3 个回复
最合适的回答,由SO网友:Jumi 整理而成

以下是我的发现:

这个ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION 错误是Content-Disposition 标题。以下是正确的:

array(
    \'Content-Disposition\' => \'attachment; filename="\' . $filename . \'"\'
)
请注意semicolon 连接后doublequotes 围绕文件名和capital D 正如@TomJ Nowell所指出的,在性格方面。

这解决了标题问题,但WP_REST_Response 仍然像json编码一样输出文件。所以我按照传统的方式:

header(\'Content-Disposition: attachment; filename="\' . $filename . \'"\');
header(\'Content-Type: application/octet-stream\');
readfile($path);
exit;

SO网友:Tom J Nowell

添加内容处置字段就足够了。

但具体来说是内容-Di位置非内容-dI位置

我还想对传递给的filename参数添加一些验证file_get_contents 以确保其存在且有效。否则,您可能容易受到目录遍历攻击或远程URL请求的攻击

SO网友:Walf

对于这种情况,有一个钩子可以避免过早停止脚本执行,并维护WP对HEAD 方法请求。在REST响应处理程序函数中放入类似的内容:

$your_served = false;
$your_content = $something;

add_filter(
    \'rest_pre_serve_request\',
    function($served, $result, $request, $rest_server) use (&$your_served, &$your_content) {
        if (!$served) {
            if ($your_served) {
                $served = $your_served;
            }
            else {
                add_filter(
                    \'rest_pre_echo_response\',
                    function($result, $rest_server, $request) use (&$your_served, &$your_content) {
                        # serve $your_content here but do not return or exit, in this case readfile() would be optimal
                        $your_served = true;
                        return null; # explicitly prevent WP sending the normal JSON response
                    },
                    10, # change priority to suit
                    3
                );
            }
        }
        return $served;
    },
    10, # change priority to suit
    4
);

$result = new WP_REST_Response();
$result->set_headers([
    \'Content-Type\' => $your_content_type,
    # you may include others such as \'Content-Length\'
]);
return $result;

结束