【问题标题】:Error 406 Not Acceptable with idHTTP on Android在 Android 上使用 idHTTP 时出现错误 406 不可接受
【发布时间】:2017-04-12 14:36:58
【问题描述】:

我正在尝试使用 idHTTP 和 PHP 脚本在 MySQL 数据库上发布插入。这是要插入数据库的 PHP 脚本:

    $mysqli = new mysqli($servidor, $usuario, $senha, $banco);

    // Caso algo tenha dado errado, exibe uma mensagem de erro
    if (mysqli_connect_errno()) trigger_error(mysqli_connect_error());

    $iduser         = quoted_printable_decode($_POST['iduser']);
    $nome           = quoted_printable_decode($_POST['nome']);
    $data           = quoted_printable_decode($_POST['data']);
    $hora           = quoted_printable_decode($_POST['hora']);
    $mensagem       = quoted_printable_decode($_POST['mensagem']);
    $latitude       = quoted_printable_decode($_POST['latitude']);
    $longitude      = quoted_printable_decode($_POST['longitude']);
    $imagem         = $_FILES["imagem"]['tmp_name'];
    $tamanho        = $_FILES['imagem']['size'];

    header($_SERVER["SERVER_PROTOCOL"] . " 200 OK"); 
    header('Content-Type: text/plain; charset="utf-8"');

    if ( $imagem != "none" )
    {
        $fp = fopen($imagem, "rb");
        $conteudo = fread($fp, $tamanho);
        $conteudo = addslashes($conteudo);
        fclose($fp);

        $queryInsercao = "INSERT INTO tabpainel (iduser, nome, data, hora, mensagem, latitude, longitude, imagem) VALUES ('$iduser', '$nome', '$data','$hora','$mensagem', '$latitude', '$longitude', '$conteudo')";

        mysqli_query($mysqli,$queryInsercao) or die("Algo deu errado ao inserir o registro. Tente novamente.");

        if (mysqli_affected_rows($mysqli) > 0)
                include 'baixarpainel.php';
            else
                print utf8_encode("Não foi possível inserir o registro");
        }
        else
            print utf8_encode("Não foi possível carregar a imagem.");
  ?>

在 Delphi 中,我正在使用这个:

      FormPHP := TIdMultiPartFormDataStream.Create;

      FormPHP.AddFile       ('imagem',    AImagem,    'image/jpeg');
      FormPHP.AddFormField  ('iduser',    AIDUser,    'utf-8');
      FormPHP.AddFormField  ('nome',      ANome,      'utf-8');
      FormPHP.AddFormField  ('data',      AData,      'utf-8');
      FormPHP.AddFormField  ('hora',      AHora,      'utf-8');
      FormPHP.AddFormField  ('mensagem',  AMensagem,  'utf-8');
      FormPHP.AddFormField  ('latitude',  '1');
      FormPHP.AddFormField  ('longitude', '1');

      Response := TStringStream.Create('',TEncoding.UTF8);

      HTTP:= TIdHTTP.Create(self);
 HTTP.Post('http://addressexample.com/cadastro.php',FormPHP,Response);

在不得不更改托管公司之前,它运行良好。 Hostinger 没问题,但 Hostgator 不行。使用 Hostgator,idHTTP 在 EIdHTTPProtocalException 类中引发异常,消息为:“HTTP/1.1 406 Not Acceptable”。 Hostgator 支持已禁用 mod_security,这可能会导致问题。

此异常仅发生在 Android 上。在 Windows 上使用相同的应用程序,它工作正常。

更新:我尝试了另一件事。 PHP 脚本是这样的:

    // Conecta-se ao banco de dados MySQL
    $mysqli = new mysqli($servidor, $usuario, $senha, $banco);

    // Caso algo tenha dado errado, exibe uma mensagem de erro
    if (mysqli_connect_errno()) trigger_error(mysqli_connect_error());

    # Instanciando o XMLWriter
    $xml = new XMLWriter;
    $xml->openMemory();

    # Definindo o encoding do XML
    $xml->startDocument( '1.0', 'UTF-8');

    # Primeiro elemento do XML
    $xml->startElement("DATAPACKET");
    $xml->writeAttribute("version", "2.0");
        $xml->StartElement("METADATA");
            $xml->startElement("FIELDS");
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "id");
                    $xml->writeAttribute("fieldtype", "I4");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "iduser");
                    $xml->writeAttribute("fieldtype", "String");
                    $xml->writeAttribute("Width", "30");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "nome");
                    $xml->writeAttribute("fieldtype", "String");
                    $xml->writeAttribute("Width", "200");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "data");
                    $xml->writeAttribute("fieldtype", "String");
                    $xml->writeAttribute("Width", "8");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "hora");
                    $xml->writeAttribute("fieldtype", "String");
                    $xml->writeAttribute("Width", "5");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "mensagem");
                    $xml->writeAttribute("fieldtype", "String");
                    $xml->writeAttribute("Width", "3000");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "latitude");
                    $xml->writeAttribute("fieldtype", "r8");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "longitude");
                    $xml->writeAttribute("fieldtype", "r8");
                $xml->endElement();
                $xml->startElement("FIELD");
                    $xml->writeAttribute("attrname", "imagem");
                    $xml->writeAttribute("fieldtype", "bin.hex");
                    $xml->writeAttribute("subtype", "Binary");
                $xml->endElement();
            $xml->endElement(); //FIELDS
        $xml->endElement(); //METADATA

        $xml->StartElement("ROWDATA"); 
        # Query na tabela escolhida
        $rs_table = $mysqli->query("select * from tabpainel ORDER BY id DESC LIMIT 50");
        while($table = $rs_table->fetch_array(MYSQLI_ASSOC))
            {
                # Transformando array em objeto
                $table = (object)$table;
                # Criando elemento tabela
                $xml->StartElement("ROW");
                # Setando os atributos
                    $xml->writeAttribute("id", "$table->id");
                    $xml->writeAttribute("iduser", "$table->iduser");
                    $xml->writeAttribute("nome", "$table->nome");
                    $xml->writeAttribute("data", "$table->data");
                    $xml->writeAttribute("hora", "$table->hora");
                    $xml->writeAttribute("mensagem", "$table->mensagem");
                    $xml->writeAttribute("latitude", "$table->latitude");
                    $xml->writeAttribute("longitude","$table->longitude");
                    $xml->writeAttribute("imagem", base64_encode("$table->imagem"));
                $xml->endElement();
            }
        # Fechando o ROWDATA
        $xml->endElement();
    # Fechando o elemento DATAPACKET
    $xml->endElement();
    # Encerrando a conexao
    //$con->close();
    # Definindo cabecalho de saida
    header("content-type: application/xml; charset=utf-8");
    # Imprimindo a saida do XML
    print $xml->outputMemory(true);
?>

我使用 http.get 来接收 xml:

Http.HandleRedirects:= true;
Http.request.useragent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MAAU)';
MS.Text:= Http.get('http://addressexample.com/baixarpainel.php');
 MS.SaveToFile(FarquivoBaixado);

这在 Android 上也可以正常工作。问题仍然只是 Android 上的 http.post。

【问题讨论】:

  • 题外话,但请使用 PDO 而不是 mysqli,如果您从未听说过,请在 Google 上搜索。它将改善您的代码和安全性:)
  • 谢谢阿尔贝托。我会谷歌它。

标签: php android delphi indy idhttp


【解决方案1】:

我可以通过简单地更改用户代理字符串来解决这个问题。我认为远程机器(服务器)安装了一些安全性。

我只是将 Request -> UserAgent 设置为:

Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0

现在它可以工作了!

【讨论】:

    【解决方案2】:

    经过长时间的尝试解决,这就是发生的事情。

    在 Windows 上,应用程序运行良好,当我尝试使用 HTTP.GET 时,我想这不可能是服务器端的问题,而是我的请求。因此,我开始使用以下代码在 Windows 和 Android 上打开请求和响应的标头:

    Astr.Add('Response text: ' + Http.Response.ResponseText);
    Astr.Add(#13);  
    Astr.Add('Raw Headers Response: ' + HTTP.Response.RawHeaders.Text);
    Astr.Add(#13);
    Astr.Add('Raw Headers Request: ' + HTTP.Request.RawHeaders.Text);
    

    这是我在 Android 上收到的消息:

    响应文本:HTTP/1.1 406 不可接受

    原始标头响应:服务器:nginx/1.10.2
    日期:...
    内容类型:text/html;
    字符集=iso-8859-1
    内容长度:226
    连接:保持活动

    原始标头请求:连接:保持活动
    内容类型:multipart/form-data;边界=--------(一些数字)
    内容长度:0 主机:我的主机 接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 接受编码:身份
    用户代理:...

    这是 Windows 上的消息

    响应文本:HTTP/1.1 200 OK

    原始标头请求:服务器:nginx/1.10.2 日期: ... 内容类型:应用程序/xml 连接:关闭 变化:Accept-Encoding、User-Agent

    原始标头请求:连接:保持活动

    内容类型:multipart/form-data;边界=--------(数字) 内容长度:0 主机:我的主机 接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 用户代理:...

    不同平台上的同一个应用在请求上发送不同的标头。在 Android 上,idHTTP 发送了一个在 Windows 上没有的 Accept-Encoding。所以我尝试在 HTTP.POST 之前添加:Http.Request.AcceptEncoding:= '*';,它起作用了!我按预期收到了 xml。

    我不知道为什么 idHTTP 会改变 Request Accept-Encoding,但我不得不指定另一个,因为 MDN 定义我选择了这个:

    *

    匹配标头中尚未列出的任何内容编码。这是 如果标头不存在,则为默认值。这并不意味着 支持任何算法;只是没有表达偏好。

    【讨论】:

    • 您必须使用旧版本的 Indy,并且应该升级到最新版本。 Indy 大约在 2 年前进行了更新,不再发送 Accept-Encoding: identity,除非您明确将 Request.AcceptEncoding 设置为 identity 或分配了 Compressor 来处理压缩响应。 identity 默认不再发送,因为某些服务器没有正确处理它。此外,您的 Android 应用程序发送 identity 但您的 Windows 应用程序并不意味着您可能在每个应用程序中使用不同版本的 Indy。如果它们是相同的版本,它们应该发送相同的默认标头
    • 我正在使用 Delphi Seattle 和 Indy 10.6.2.5298。
    • 当前的 Indy 版本是 10.6.2.5377。默认的 identity 行为在 5321 中发生了变化。所以你落后于更新,应该升级。
    【解决方案3】:

    TIdHTTP 在所有平台上的工作方式完全相同,因为 Indy 使用单个跨平台代码库。所以生成的 HTTP 请求在所有平台上应该是完全一样的。

    当 HTTP 请求包含 Accept 标头但未指定服务器能够呈现响应的任何媒体类型时,会发生 HTTP 406 错误。根据RFC 2616 Section 14.1

    如果不存在 Accept 头字段,则假定客户端接受所有媒体类型。如果存在 Accept 头字段,并且如果服务器无法发送根据组合 Accept 字段值可接受的响应,则服务器应该发送 406(不可接受)响应。

    您的 PHP 脚本正在发送 text/plain 响应,因此如果您发送不允许 text/plainAccept 标头,则可能会导致 406 错误。听起来 Hostgator 比 Hostinger 执行得更多。

    默认情况下,TIdHTTP 将其Request.Accept 属性设置为以下字符串值:

    'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
    

    这在技术上允许所有媒体类型通过*/*,但其优先级低于其他一些媒体类型。但是,如果服务器正确实现Accept 处理,该默认值应该仍然足以允许text/plain 响应。

    您需要联系 Hostgator 并与他们讨论问题,因为问题是他们的问题,而不是您的问题。

    话虽如此,既然您知道服务器响应始终是 text/plain,您可以在调用 Post() 之前将以下内容添加到您的代码中:

    HTTP.Request.Accept := 'text/plain';
    HTTP.Request.AcceptCharset := 'utf-8';
    

    【讨论】:

    • 这很奇怪,因为在 Windows 上没问题,问题只在 Android 上。这是同一个应用程序。我已经添加了接受和接受字符集,但也没有工作。我会尝试再次联系 Hostgator,但他们已经告诉我他们不知道可能出了什么问题。
    • 雷米,请检查我的回答。
    • @wordermorr 任何体面的系统管理员都应该能够检查服务器日志和跟踪请求。如果 HostGator 说他们无法解决您的应用程序对其服务器的请求,您应该寻找另一个提供商。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 2015-10-15
    • 2010-12-02
    • 2013-12-07
    • 1970-01-01
    • 2013-12-18
    相关资源
    最近更新 更多