Żądania HTTP z file_get_contents, pobierające kod odpowiedzi

88

Próbuję używać file_get_contentsrazem z stream_context_createdo wykonywania żądań POST. Mój kod do tej pory:

    $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);

Działa dobrze, jednak gdy wystąpi błąd HTTP, wypluwa ostrzeżenie:

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

i zwraca fałsz. Czy jest sposób, aby:

  • pominąć ostrzeżenie (planuję zgłosić własny wyjątek w przypadku niepowodzenia)
  • uzyskać informacje o błędzie (przynajmniej kod odpowiedzi) ze strumienia
Georg
źródło

Odpowiedzi:

152

http://php.net/manual/en/reserved.variables.httpresponseheader.php

file_get_contents("http://example.com", false, stream_context_create(['http' => ['ignore_errors' => true]]));
var_dump($http_response_header);
Alles
źródło
35
Dla tych, zastanawiając się, że odpowiedź na pierwsze pytanie jest dodanie 'ignore_errors' => TRUEdo $options.
georg
4
@georg Możesz również umieścić @na początku wiersza.
FluorescentGreen5
21

Żadna z odpowiedzi (w tym ta zaakceptowana przez OP) nie spełnia w rzeczywistości dwóch wymagań:

  • pominąć ostrzeżenie (planuję zgłosić własny wyjątek w przypadku niepowodzenia)
  • uzyskać informacje o błędzie (przynajmniej kod odpowiedzi) ze strumienia

Oto moja opinia:

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;
}

Spowoduje to brak 200odpowiedzi, ale możesz łatwo z tego miejsca pracować, np. Dodać prostą Responseklasę i return new Response((int) $status, $response);jeśli to lepiej pasuje do twojego przypadku użycia.

Na przykład, aby wykonać JSON POSTdo punktu końcowego API:

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

Zwróć uwagę na użycie "ignore_errors" => truew httpmapie kontekstu - zapobiegnie to generowaniu przez funkcję błędów dla kodów stanu innych niż 2xx.

Jest to najprawdopodobniej „właściwa” wielkość pomijania błędów dla większości przypadków użycia - nie polecam używania @operatora eliminacji błędów, ponieważ spowoduje to również wyeliminowanie błędów, takich jak zwykłe przekazanie niewłaściwych argumentów, które mogą przypadkowo ukryć błąd w kod wywołujący.

mindplay.dk
źródło
5

Dodanie kilku kolejnych wierszy do zaakceptowanej odpowiedzi w celu uzyskania kodu 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);

aby ukryć wyjście błędu, oba komentarze są w porządku, ignore_errors = true lub @ (wolę @)

hamboy75
źródło
-1

Wchodzę na tę stronę z innym problemem, więc zamieszczam swoją odpowiedź. Mój problem polegał na tym, że próbowałem po prostu wyłączyć ostrzeżenie i wyświetlić dostosowany komunikat ostrzegawczy dla użytkownika, więc ta prosta i oczywista poprawka pomogła mi:

// Suppress the warning messages
error_reporting(0);

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

W razie potrzeby cofnij raportowanie błędów po tym:

// Enable warning messages again
error_reporting(-1);
Kaarel
źródło
-2

@file_get_contentsi ignore_errors = truenie są takie same: pierwsze nic nie zwraca; druga blokuje komunikaty o błędach, ale zwraca odpowiedź serwera (np. 400 Bad request).

Używam takiej funkcji:

$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;

Zwraca 200 (Ok) lub 400 (Złe żądanie).

Działa doskonale i jest łatwiejszy niż cURL.

Besen
źródło
Jak to rozwiązuje problem? Czy możesz wyjaśnić, jak uzyskać kod odpowiedzi?
Nico Haase
@Nico Co myślisz o moim rozwiązaniu?
Besen
Nadal jest zły, ponieważ nie analizujesz wyniku JSON i czytasz losowe pole o nazwie status- nie ma połączenia z kodem statusu HTTP dla wywołania API
Nico Haase
Jest oczywiste, że interfejs API RESTful, z którym się łączę, zwraca odpowiedź JSON i że to, co muszę wiedzieć do mojego celu (200 lub 400), jest zawarte w polu „status”. To wszystko, czego potrzebuję i to wszystko, co otrzymuję. Jeśli potrzebujesz znać kod stanu HTTP, po prostu zrób to: return $ http_response_header [0]; Ale pamiętaj, że nie działa z @file_get_contents.
Besen
Jak to odpowiada na pierwotne pytanie? Jak myślisz, dlaczego ta $http_response_header[0], bardzo pozytywna odpowiedź, nie działa file_get_contents?
Nico Haase