Richieste HTTP con file_get_contents, ottenendo il codice di risposta


88

Sto cercando di utilizzare file_get_contentsinsieme a stream_context_createper effettuare richieste POST. Il mio codice finora:

    $options = array('http' => array(
        'method'  => 'POST',
        'content' => $data,
        'header'  => 
            "Content-Type: text/plain\r\n" .
            "Content-Length: " . strlen($data) . "\r\n"
    ));
    $context  = stream_context_create($options);
    $response = file_get_contents($url, false, $context);

Funziona bene, tuttavia, quando si verifica un errore HTTP, emette un avviso:

file_get_contents(...): failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request

e restituisce false. C'è un modo per:

  • sopprimere un avviso (ho intenzione di lanciare la mia eccezione in caso di errore)
  • ottenere le informazioni sull'errore (almeno il codice di risposta) dal flusso

Risposte:



21

Nessuna delle risposte (compresa quella accettata da OP) soddisfa effettivamente i due requisiti:

  • sopprimere un avviso (ho intenzione di lanciare la mia eccezione in caso di errore)
  • ottenere le informazioni sull'errore (almeno il codice di risposta) dal flusso

Ecco la mia opinione:

function fetch(string $method, string $url, string $body, array $headers = []) {
    $context = stream_context_create([
        "http" => [
            // http://docs.php.net/manual/en/context.http.php
            "method"        => $method,
            "header"        => implode("\r\n", $headers),
            "content"       => $body,
            "ignore_errors" => true,
        ],
    ]);

    $response = file_get_contents($url, false, $context);

    /**
     * @var array $http_response_header materializes out of thin air
     */

    $status_line = $http_response_header[0];

    preg_match('{HTTP\/\S*\s(\d{3})}', $status_line, $match);

    $status = $match[1];

    if ($status !== "200") {
        throw new RuntimeException("unexpected response status: {$status_line}\n" . $response);
    }

    return $response;
}

Questo genererà una mancata 200risposta, ma puoi facilmente lavorare da lì, ad esempio aggiungi una Responseclasse semplice e return new Response((int) $status, $response);se questo si adatta meglio al tuo caso d'uso.

Ad esempio, per eseguire un JSON POSTsu un endpoint API:

$response = fetch(
    "POST",
    "http://example.com/",
    json_encode([
        "foo" => "bar",
    ]),
    [
        "Content-Type: application/json",
        "X-API-Key: 123456789",
    ]
);

Nota l'uso di "ignore_errors" => truenella httpmappa di contesto: ciò impedirà alla funzione di generare errori per codici di stato non 2xx.

Questa è molto probabilmente la quantità "giusta" di soppressione degli errori per la maggior parte dei casi d'uso - Non consiglio di usare l' @operatore di soppressione degli errori, poiché questo sopprimerà anche errori come il semplice passaggio di argomenti sbagliati, che potrebbero inavvertitamente nascondere un bug in codice chiamante.


5

Aggiungendo qualche riga in più alla risposta accettata per ottenere il codice http

function getHttpCode($http_response_header)
{
    if(is_array($http_response_header))
    {
        $parts=explode(' ',$http_response_header[0]);
        if(count($parts)>1) //HTTP/1.0 <code> <text>
            return intval($parts[1]); //Get code
    }
    return 0;
}

@file_get_contents("http://example.com");
$code=getHttpCode($http_response_header);

per nascondere l'output dell'errore entrambi i commenti sono ok, ignore_errors = true o @ (preferisco @)


-1

Vado a questa pagina con una specie di problema diverso, quindi inserisco la mia risposta. Il mio problema era che stavo solo cercando di sopprimere la notifica di avviso e visualizzare un messaggio di avviso personalizzato per l'utente, quindi questa soluzione semplice ed ovvia mi ha aiutato:

// Suppress the warning messages
error_reporting(0);

$contents = file_get_contents($url);
if ($contents === false) {
  print 'My warning message';
}

E se necessario, ripristina la segnalazione degli errori dopo di che:

// Enable warning messages again
error_reporting(-1);

-2

@file_get_contentse ignore_errors = truenon sono la stessa cosa: la prima non restituisce nulla; il secondo sopprime i messaggi di errore, ma restituisce la risposta del server (es. 400 Bad request).

Uso una funzione come questa:

$result = file_get_contents(
  $url_of_API, 
  false, 
  stream_context_create([
    'http' => [
      'content' => json_encode(['value1' => $value1, 'value2' => $value2]), 
      'header' => 'Authorization: Basic XXXXXXXXXXXXXXX', 
      'ignore_errors' => 1, 
      'method' => 'POST', 
      'timeout' => 10
    ]
  ])
);

return json_decode($result)->status;

Restituisce 200 (Ok) o 400 (Richiesta non valida).

Funziona perfettamente ed è più facile di cURL.


E come risolve la domanda? Puoi spiegare come ottenere il codice di risposta?
Nico Haase

@Nico Cosa ne pensi della mia soluzione?
Besen il

È ancora pessimo, poiché analizzi un risultato JSON non e leggi un campo casuale denominato status- non ha connessione al codice di stato HTTP per la chiamata API
Nico Haase

È ovvio che l'API RESTful a cui mi connetto restituisce una risposta JSON e che ciò che devo sapere per il mio scopo (200 o 400) è contenuto nel campo "status". È tutto ciò di cui ho bisogno ed è tutto ciò che ottengo. Se hai bisogno di conoscere il codice di stato HTTP, fallo semplicemente: return $ http_response_header [0]; Ma nota che non funziona con @file_get_contents.
Besen il

E come risponde alla domanda originale? Perché pensi che $http_response_header[0], una risposta altamente votata, non funzioni file_get_contents?
Nico Haase
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.