错误406在Android上的idHTTP不可接受

时间:2016-11-28 19:06:52

标签: php android delphi indy idhttp

我正在尝试使用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。

3 个答案:

答案 0 :(得分:3)

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

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

  

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

您的PHP脚本正在发送text/plain响应,因此如果您发送的Accept标头不允许text/plain,那么可能会导致406错误。听起来Hostgator的执行力超过了Hostinger。

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

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

技术上允许所有媒体类型通过*/*,但优先级低于其他媒体类型。但是,如果服务器正确地执行text/plain处理,那么默认值应该足以允许Accept响应。

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

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

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

答案 1 :(得分:0)

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

在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;边界= ------(somenumbers)
  内容长度:0   主持人:myhost   接受:text / html的,应用/ XHTML + xml的,应用/ XML; Q = 0.9, / 的; Q = 0.8   Accept-Encoding:身份
  用户代理:...

这是Windows上的消息

  

响应文本:HTTP / 1.1 200 OK

     

原始标头请求:服务器:nginx / 1.10.2   日期:......   Content-Type:application / xml   连接:关闭   变化:接受编码,用户代理

     

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

     

内容类型:multipart / form-data;边界= --------(numebrs)   内容长度:0   主持人:myhost   接受:text / html的,应用/ XHTML + xml的,应用/ XML; Q = 0.9, / 的; Q = 0.8   用户代理:...

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

我不知道为什么idHTTP正在改变请求接受编码,但我必须指定另一个,因为MDN定义我选择了这个:

  

*

     

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

答案 2 :(得分:0)

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

我只是设置了请求 - &gt; UserAgent:

Mozilla / 5.0(Android 4.4;移动版; rv:41.0)Gecko / 41.0 Firefox / 41.0

现在它有效!