Android 上的 idHTTP 错误 406 不可接受
Error 406 Not Acceptable with idHTTP on Android
我正在尝试使用 idHTTP 和 PHP 脚本 post 在 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 在 class 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。
TIdHTTP
在所有平台上的工作方式完全相同,因为 Indy 使用单个 cross-platform 代码库。所以生成的HTTP请求在所有平台上应该是完全一样的。
当 HTTP 请求包含 Accept
header 且未指定服务器能够在其中呈现响应的任何媒体类型时,会发生 HTTP 406
错误。Per RFC 2616 Section 14.1:
If no Accept header field is present, then it is assumed that the client accepts all media types. If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.
您的 PHP 脚本正在发送一个 text/plain
响应,因此如果您发送一个 Accept
header 不允许 text/plain
那么这可能会导致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 上,应用程序工作正常,当我尝试使用 HTTP.GET 时,我想这不可能是服务器端的问题,但应我的要求。然后,我开始使用以下代码在 Windows 和 Android 上打开请求和响应的 headers:
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:
上收到的消息
Response text: HTTP/1.1 406 Not Acceptable
Raw Headers Response: Server: nginx/1.10.2
Date: ...
Content-Type: text/html;
charset=iso-8859-1
Content-Length:226
Connection: keep-alive
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=------(somenumbers)
Content-Length: 0
Host: myhost
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: identity
User-Agent: ...
这是 Windows
上的消息
Response text: HTTP/1.1 200 OK
Raw Headers Request: Server: nginx/1.10.2
Date: ...
Content-Type: application/xml
Connection: close
Vary: Accept-Encoding,User-Agent
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------(numebrs)
Content-Length: 0
Host: myhost
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
User-Agent: ...
同一应用在不同平台上发送不同的 headers 请求。在 Android 上,idHTTP 发送了一个 Accept-Encoding,而在 Windows 上则没有。所以我尝试在 HTTP.POST 之前添加: Http.Request.AcceptEncoding:= '*';
并且成功了!正如预期的那样,我收到了 xml。
我不知道为什么 idHTTP 会更改请求 Accept-Encoding,但我必须指定另一个,因为 MDN 定义我选择了这个:
*
Matches any content encoding not already listed in the header. This is
the default value if the header is not present. It doesn't mean that
any algorithm is supported; merely that no preference is expressed.
我可以通过简单地更改用户代理字符串来解决这个问题。我认为远程机器(服务器)安装了一些安全措施。
我刚刚将 Request -> UserAgent 设置为:
Mozilla/5.0 (Android 4.4; 手机; rv:41.0) Gecko/41.0 Firefox/41.0
现在可以了!
我正在尝试使用 idHTTP 和 PHP 脚本 post 在 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 在 class 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。
TIdHTTP
在所有平台上的工作方式完全相同,因为 Indy 使用单个 cross-platform 代码库。所以生成的HTTP请求在所有平台上应该是完全一样的。
当 HTTP 请求包含 Accept
header 且未指定服务器能够在其中呈现响应的任何媒体类型时,会发生 HTTP 406
错误。Per RFC 2616 Section 14.1:
If no Accept header field is present, then it is assumed that the client accepts all media types. If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.
您的 PHP 脚本正在发送一个 text/plain
响应,因此如果您发送一个 Accept
header 不允许 text/plain
那么这可能会导致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 上,应用程序工作正常,当我尝试使用 HTTP.GET 时,我想这不可能是服务器端的问题,但应我的要求。然后,我开始使用以下代码在 Windows 和 Android 上打开请求和响应的 headers:
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:
上收到的消息Response text: HTTP/1.1 406 Not Acceptable
Raw Headers Response: Server: nginx/1.10.2
Date: ...
Content-Type: text/html;
charset=iso-8859-1
Content-Length:226
Connection: keep-aliveRaw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=------(somenumbers)
Content-Length: 0 Host: myhost Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Encoding: identity
User-Agent: ...
这是 Windows
上的消息Response text: HTTP/1.1 200 OK
Raw Headers Request: Server: nginx/1.10.2 Date: ... Content-Type: application/xml Connection: close Vary: Accept-Encoding,User-Agent
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------(numebrs) Content-Length: 0 Host: myhost Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 User-Agent: ...
同一应用在不同平台上发送不同的 headers 请求。在 Android 上,idHTTP 发送了一个 Accept-Encoding,而在 Windows 上则没有。所以我尝试在 HTTP.POST 之前添加: Http.Request.AcceptEncoding:= '*';
并且成功了!正如预期的那样,我收到了 xml。
我不知道为什么 idHTTP 会更改请求 Accept-Encoding,但我必须指定另一个,因为 MDN 定义我选择了这个:
*
Matches any content encoding not already listed in the header. This is the default value if the header is not present. It doesn't mean that any algorithm is supported; merely that no preference is expressed.
我可以通过简单地更改用户代理字符串来解决这个问题。我认为远程机器(服务器)安装了一些安全措施。
我刚刚将 Request -> UserAgent 设置为:
Mozilla/5.0 (Android 4.4; 手机; rv:41.0) Gecko/41.0 Firefox/41.0
现在可以了!