Jak wysłać żądanie POST za pomocą PHP?

656

Właściwie chcę przeczytać treść, która pojawia się po zapytaniu, kiedy to jest zrobione. Problem polega na tym, że adres URL akceptuje tylko POSTmetody i nie podejmuje żadnych działań z GETmetodą ...

Muszę przeczytać całą zawartość za pomocą domdocumentlub file_get_contents(). Czy jest jakaś metoda, która pozwoli mi przesłać parametry za pomocą POSTmetody, a następnie odczytać zawartość za pośrednictwem PHP?

Fred Tanrikut
źródło

Odpowiedzi:

1260

Metoda bez CURL z PHP5:

$url = 'http://server.com/path';
$data = array('key1' => 'value1', 'key2' => 'value2');

// use key 'http' even if you send the request to https://...
$options = array(
    'http' => array(
        'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
        'method'  => 'POST',
        'content' => http_build_query($data)
    )
);
$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) { /* Handle error */ }

var_dump($result);

Zobacz podręcznik PHP, aby uzyskać więcej informacji na temat metody i sposobu dodawania nagłówków, na przykład:

dbau
źródło
64
Warto zauważyć, że jeśli zdecydujesz się użyć tablicy dla nagłówków, NIE kończ kluczy ani wartości literą „\ r \ n”. stream_context_create () zabierze tekst tylko do pierwszego '\ r \ n'
raptor
11
Adres URL może być użyty jako nazwa pliku file_get_contents()tylko wtedy, gdy włączone są opakowania fopen. Zobacz php.net/manual/en/…
Pino,
3
@I lovefile_get_contents()
impas
14
Czy istnieje konkretny powód, aby nie używać CURL?
jvannistelrooy
37
@jvannistelrooy CURL dla PHP to rozszerzenie, które może nie istnieć we wszystkich środowiskach, podczas gdy file_get_contents()jest częścią jądra PHP. Niepotrzebne użycie rozszerzenia może poszerzyć powierzchnię ataku Twojej aplikacji. Np. Google php curl cve
Pocketsand
139

Możesz użyć cURL :

<?php
//The url you wish to send the POST request to
$url = $file_name;

//The data you want to send via POST
$fields = [
    '__VIEWSTATE '      => $state,
    '__EVENTVALIDATION' => $valid,
    'btnSubmit'         => 'Submit'
];

//url-ify the data for the POST
$fields_string = http_build_query($fields);

//open connection
$ch = curl_init();

//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);

//So that curl_exec returns the contents of the cURL; rather than echoing it
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); 

//execute post
$result = curl_exec($ch);
echo $result;
?>
Fred Tanrikut
źródło
3
ten działał dla mnie, ponieważ strona, którą wysyłam na stronę, która nie ma treści, więc wersja file_get_contents nie działała.
CommentLuv
9
file_get_contents rozwiązanie nie działa na konfiguracjach PHP z allow_url_fopen Off (jak w hostingu współdzielonym). Ta wersja korzysta z biblioteki curl i myślę, że jest najbardziej „uniwersalna”, więc oddaję mój głos
Dayron Gallardo
81
Nie masz witryny, w której skopiowałeś ten przykładowy kod z: davidwalsh.name/curl-post
efreed
4
Chociaż nie jest to bardzo ważne, dane parametru CURLOPT_POSTFIELDS w rzeczywistości nie muszą być konwertowane na ciąg znaków („urlified”). Cytat: „Ten parametr może być przekazany jako ciąg znaków w postaci adresu URL, na przykład„ para1 = val1 i para2 = val2 & ... ”lub jako tablica z nazwą pola jako kluczem, a dane pola jako wartością. Jeśli wartość jest tablicą, typ treści nagłówek zostanie ustawiony na multipart / form-data. ” Link: php.net/manual/en/function.curl-setopt.php .
Edward
2
Nie ma też przestępstwa za pisanie go inaczej, ale nie wiem, dlaczego parametr CURLOPT_POST jest tutaj podany jako liczba, ponieważ mówi, aby ustawić go na wartość logiczną na stronie podręcznika. Cytat: „CURLOPT_POST: PRAWDA, aby wykonać zwykły test POST HTTP”. Link: php.net/manual/en/function.curl-setopt.php .
Edward
68

Korzystam z następującej funkcji, aby wysyłać dane za pomocą curl. $ data to tablica pól do opublikowania (zostanie poprawnie zakodowana przy użyciu http_build_query). Dane są kodowane przy użyciu application / x-www-form-urlencoded.

function httpPost($url, $data)
{
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($curl);
    curl_close($curl);
    return $response;
}

@Edward wspomina, że ​​http_build_query można pominąć, ponieważ curl poprawnie koduje tablicę przekazaną do parametru CURLOPT_POSTFIELDS, ale należy pamiętać, że w takim przypadku dane będą kodowane przy użyciu danych wieloczęściowych / formularzy.

Używam tej funkcji z interfejsami API, które oczekują kodowania danych za pomocą application / x-www-form-urlencoded. Dlatego używam http_build_query ().

Dima L.
źródło
Przekazywanie tablicy do CURLOPT_POSTFIELDS powoduje, że dane są kodowane przy użyciu danych wieloczęściowych / formularzy, co może być niepożądane.
Dima L.,
Użytkownik poprosił o file_get_contents, więc potrzebuje rozwiązania do zmiany default_stream_context
Radon8472,
Aby wyjaśnić: myślę, że @DimaL. odpowiada na komentarz, który został usunięty; http_build_querykonwertuje $datatablicę na ciąg znaków, unikając danych wyjściowych jako danych wieloczęściowych / formularzy.
ToolmakerSteve
@ Radon8472 - ... CURLOPT_RETURNTRANSFER, truepowoduje zawarcie $responsezawartości.
ToolmakerSteve
@ToolmakerSteve, jak powiedziałem, pytanie dotyczyło, file_get_contentsa twoje rozwiązanie wymaga CURL tego, czego wiele osób nie ma. więc twoje rozwiązanie może działa, ale nie odpowiada na pytanie, jak to zrobić za pomocą wbudowanych funkcji plików / strumieni.
Radon8472,
42

Polecam używać żłobka z pakietem open source, który jest w pełni testowany jednostkowo i wykorzystuje najnowsze praktyki kodowania.

Instalowanie pyska

Przejdź do wiersza poleceń w folderze projektu i wpisz następujące polecenie (zakładając, że masz już menedżer pakietów kompozytor zainstalowany). Jeśli potrzebujesz pomocy w instalacji Composer, powinieneś zajrzeć tutaj .

php composer.phar require guzzlehttp/guzzle

Używanie Guzzle do wysłania żądania POST

Korzystanie z Guzzle jest bardzo proste, ponieważ wykorzystuje lekki obiektowy interfejs API:

// Initialize Guzzle client
$client = new GuzzleHttp\Client();

// Create a POST request
$response = $client->request(
    'POST',
    'http://example.org/',
    [
        'form_params' => [
            'key1' => 'value1',
            'key2' => 'value2'
        ]
    ]
);

// Parse the response object, e.g. read the headers, body, etc.
$headers = $response->getHeaders();
$body = $response->getBody();

// Output headers and body for debugging purposes
var_dump($headers, $body);
Andreas
źródło
7
Przydałoby się wiedzieć, jakie ma to zalety w stosunku do już opublikowanego natywnego rozwiązania PHP, a także cURL.
artfulrobot
9
@artfulrobot: Natywne rozwiązanie PHP ma wiele problemów (np. połączenie z https, weryfikacją certyfikatu itp.). Dlatego prawie każdy programista PHP używa cURL. A dlaczego nie użyć cURL w tym przypadku? To proste: Guzzle ma prosty, łatwy, lekki interfejs, który odstrasza wszystkie te „problemy z obsługą niskiego poziomu cURL”. Prawie każdy, kto rozwija współczesne PHP, i tak używa Composer, więc używanie Guzzle jest po prostu bardzo proste.
Andreas
2
Dzięki, wiem, że Guzzle jest popularny, jednak zdarzają się przypadki, gdy kompozytor powoduje żal (np. Opracowuje wtyczki do większych projektów oprogramowania, które mogą już korzystać z (innej wersji) Guzzle lub innych zależności), więc dobrze jest znać te informacje, aby decyzja o tym, które rozwiązanie będzie najbardziej niezawodne
artfulrobot
26

Istnieje inna metoda CURL, jeśli idziesz tą drogą.

Jest to dość proste, gdy przyjrzysz się, jak działa rozszerzenie curl PHP, łącząc różne flagi z wywołaniami setopt (). W tym przykładzie mam zmienną $ xml, która zawiera kod XML, który przygotowałem do wysłania - Zamierzam opublikować zawartość tego metody testowej w przykładzie.

$url = 'http://api.example.com/services/xmlrpc/';
$ch = curl_init($url);

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);
//process $response

Najpierw zainicjowaliśmy połączenie, a następnie ustawiliśmy niektóre opcje za pomocą setopt (). Informują one PHP, że wysyłamy żądanie pocztowe i wysyłamy wraz z nim pewne dane, dostarczając je. Flaga CURLOPT_RETURNTRANSFER informuje curl, aby przekazał nam dane wyjściowe jako wartość zwracaną przez curl_exec, zamiast go wyprowadzać. Następnie wykonujemy połączenie i zamykamy połączenie - wynikiem jest odpowiedź $.

Josip Ivic
źródło
1
w 3 curl_setopt () rozmowy, pierwszy arg powinien być $chnie $curl, prawda?
jcomeau_ictx
Czy możesz użyć tego samego kodu do danych POST JSON? Ale zamień $ xml na powiedz $ json (gdzie $ json jest prawdopodobnie łańcuchem JSON?)
Neal Davis
24

Jeśli przypadkiem używasz Wordpressa do opracowania aplikacji (jest to w rzeczywistości wygodny sposób na uzyskanie autoryzacji, stron informacyjnych itp. Nawet w przypadku bardzo prostych rzeczy), możesz użyć następującego fragmentu:

$response = wp_remote_post( $url, array('body' => $parameters));

if ( is_wp_error( $response ) ) {
    // $response->get_error_message()
} else {
    // $response['body']
}

Wykorzystuje różne sposoby wykonania rzeczywistego żądania HTTP, w zależności od tego, co jest dostępne na serwerze WWW. Aby uzyskać więcej informacji, zobacz dokumentację interfejsu API HTTP .

Jeśli nie chcesz tworzyć niestandardowego motywu lub wtyczki, aby uruchomić silnik Wordpress, możesz po prostu wykonać następujące czynności w izolowanym pliku PHP w katalogu głównym Wordpress:

require_once( dirname(__FILE__) . '/wp-load.php' );

// ... your code

Nie pokaże żadnego motywu ani nie wyświetli żadnego HTML, po prostu zhakuj za pomocą API Wordpress!


źródło
22

Chciałbym dodać kilka przemyśleń na temat opartej na curl odpowiedzi Freda Tanrikuta. Wiem, że większość z nich jest już napisana w powyższych odpowiedziach, ale myślę, że dobrym pomysłem jest pokazanie odpowiedzi obejmującej wszystkie z nich razem.

Oto klasa, którą napisałem, aby wysyłać żądania HTTP-GET / POST / PUT / DELETE na podstawie curl, dotyczące tylko treści odpowiedzi:

class HTTPRequester {
    /**
     * @description Make HTTP-GET call
     * @param       $url
     * @param       array $params
     * @return      HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPGet($url, array $params) {
        $query = http_build_query($params); 
        $ch    = curl_init($url.'?'.$query);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
    /**
     * @description Make HTTP-POST call
     * @param       $url
     * @param       array $params
     * @return      HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPPost($url, array $params) {
        $query = http_build_query($params);
        $ch    = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
    /**
     * @description Make HTTP-PUT call
     * @param       $url
     * @param       array $params
     * @return      HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPPut($url, array $params) {
        $query = \http_build_query($params);
        $ch    = \curl_init();
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);
        \curl_setopt($ch, \CURLOPT_HEADER, false);
        \curl_setopt($ch, \CURLOPT_URL, $url);
        \curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, 'PUT');
        \curl_setopt($ch, \CURLOPT_POSTFIELDS, $query);
        $response = \curl_exec($ch);
        \curl_close($ch);
        return $response;
    }
    /**
     * @category Make HTTP-DELETE call
     * @param    $url
     * @param    array $params
     * @return   HTTP-Response body or an empty string if the request fails or is empty
     */
    public static function HTTPDelete($url, array $params) {
        $query = \http_build_query($params);
        $ch    = \curl_init();
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);
        \curl_setopt($ch, \CURLOPT_HEADER, false);
        \curl_setopt($ch, \CURLOPT_URL, $url);
        \curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, 'DELETE');
        \curl_setopt($ch, \CURLOPT_POSTFIELDS, $query);
        $response = \curl_exec($ch);
        \curl_close($ch);
        return $response;
    }
}

Ulepszenia

  • Użycie http_build_query do pobrania ciągu zapytania z tablicy zapytań. (Możesz również użyć samej tablicy, dlatego patrz: http://php.net/manual/en/function.curl-setopt.php )
  • Zwracanie odpowiedzi zamiast echa. Przy okazji możesz uniknąć powrotu, usuwając linię curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, prawda); . Następnie zwracana jest wartość logiczna (prawda = żądanie powiodło się, w przeciwnym razie wystąpił błąd), a odpowiedź jest powtarzana. Widzieć: http://php.net/en/manual/function.curl-exec.php
  • Wyczyść zamykanie sesji i usuwanie programu obsługi curl za pomocą curl_close . Widzieć: http://php.net/manual/en/function.curl-close.php
  • Używanie wartości boolowskich dla curl_setopt funkcji zamiast używania dowolnej liczby. (Wiem, że każda liczba, która nie jest równa zeru, jest również uważana za prawdziwą, ale użycie true generuje bardziej czytelny kod, ale to tylko moja opinia)
  • Możliwość wykonywania połączeń HTTP-PUT / DELETE (przydatne do testowania usługi RESTful)

Przykład użycia

DOSTAĆ

$response = HTTPRequester::HTTPGet("http://localhost/service/foobar.php", array("getParam" => "foobar"));

POCZTA

$response = HTTPRequester::HTTPPost("http://localhost/service/foobar.php", array("postParam" => "foobar"));

POŁOŻYĆ

$response = HTTPRequester::HTTPPut("http://localhost/service/foobar.php", array("putParam" => "foobar"));

USUNĄĆ

$response = HTTPRequester::HTTPDelete("http://localhost/service/foobar.php", array("deleteParam" => "foobar"));

Testowanie

Możesz także wykonać kilka fajnych testów serwisowych, korzystając z tej prostej klasy.

class HTTPRequesterCase extends TestCase {
    /**
     * @description test static method HTTPGet
     */
    public function testHTTPGet() {
        $requestArr = array("getLicenses" => 1);
        $url        = "http://localhost/project/req/licenseService.php";
        $this->assertEquals(HTTPRequester::HTTPGet($url, $requestArr), '[{"error":false,"val":["NONE","AGPL","GPLv3"]}]');
    }
    /**
     * @description test static method HTTPPost
     */
    public function testHTTPPost() {
        $requestArr = array("addPerson" => array("foo", "bar"));
        $url        = "http://localhost/project/req/personService.php";
        $this->assertEquals(HTTPRequester::HTTPPost($url, $requestArr), '[{"error":false}]');
    }
    /**
     * @description test static method HTTPPut
     */
    public function testHTTPPut() {
        $requestArr = array("updatePerson" => array("foo", "bar"));
        $url        = "http://localhost/project/req/personService.php";
        $this->assertEquals(HTTPRequester::HTTPPut($url, $requestArr), '[{"error":false}]');
    }
    /**
     * @description test static method HTTPDelete
     */
    public function testHTTPDelete() {
        $requestArr = array("deletePerson" => array("foo", "bar"));
        $url        = "http://localhost/project/req/personService.php";
        $this->assertEquals(HTTPRequester::HTTPDelete($url, $requestArr), '[{"error":false}]');
    }
}
mwatzer
źródło
Dla mnie jest napisane „Nieprzechwycony błąd: wywołanie niezdefiniowanej metody HTTPRequester :: HTTPost ()” . Po prostu wkleiłem twoją klasę do mojego pliku .php. Coś jeszcze muszę zrobić?
LinusGeffarth
1
Czy możesz napisać swój kod? Trudno zgadnąć, co jest nie tak, bez fragmentu kodu.
mwatzer
Jak powiedziałem, dosłownie skopiowałem twój do mojego zwykłego pliku php i dał mi ten błąd.
LinusGeffarth
1
Ok, teraz widzę problem, .. to było źle w przykładzie! Musisz wywołać HTTPRequester :: HTTPPost () zamiast HTTPRequester :: HTTPost ()
mwatzer
1
Ach Tego łatwo przeoczyć. Musiałem przeczytać komentarz jak 5x zanim dostrzegł dodatkową P . Dzięki!
LinusGeffarth
19

Inną alternatywą powyższej metody bez zwijania się jest użycie natywnych funkcji strumienia :

  • stream_context_create():

    Tworzy i zwraca kontekst strumienia z dowolnymi opcjami podanymi w ustawieniach opcji .

  • stream_get_contents():

    Identyczna z file_get_contents()tą różnicą, że stream_get_contents() działa on już otwarty strumień surowca i zwraca pozostałą zawartość w ciągu znaków, do MaxLength bajtów i rozpoczynające się w określonym offsetu .

Funkcja POST z tymi może być po prostu taka:

<?php

function post_request($url, array $params) {
  $query_content = http_build_query($params);
  $fp = fopen($url, 'r', FALSE, // do not use_include_path
    stream_context_create([
    'http' => [
      'header'  => [ // header array does not need '\r\n'
        'Content-type: application/x-www-form-urlencoded',
        'Content-Length: ' . strlen($query_content)
      ],
      'method'  => 'POST',
      'content' => $query_content
    ]
  ]));
  if ($fp === FALSE) {
    return json_encode(['error' => 'Failed to get contents...']);
  }
  $result = stream_get_contents($fp); // no maxlength/offset
  fclose($fp);
  return $result;
}
CPHPython
źródło
1
Ta metoda bez CURL działała dla mnie dobrze, aby zweryfikować reCAPTCHA z Google. Ta odpowiedź jest zbieżna z tym kodem Google: github.com/google/recaptcha/blob/master/src/ReCaptcha/…
Xavi Montero
1
Nie musisz używać, fclose()jeśli $fpjest false. Ponieważ fclose()oczekuje, że zasób jest parametrem.
Floris,
1
@Floris Edytowałem to teraz i rzeczywiście dokumentacja fclose wspomina „Wskaźnik pliku musi być poprawny”. Dziękujemy za zauważenie tego!
CPHPython,
8

Lepszy sposób wysyłania GETlub wysyłania POSTzapytań PHPjest następujący:

<?php
    $r = new HttpRequest('http://example.com/form.php', HttpRequest::METH_POST);
    $r->setOptions(array('cookies' => array('lang' => 'de')));
    $r->addPostFields(array('user' => 'mike', 'pass' => 's3c|r3t'));

    try {
        echo $r->send()->getBody();
    } catch (HttpException $ex) {
        echo $ex;
    }
?>

Kod pochodzi z oficjalnej dokumentacji tutaj http://docs.php.net/manual/da/httprequest.send.php

Imran Zahoor
źródło
1
@akinuri dzięki za wyróżnienie, zamierzam udostępnić nowy.
Imran Zahoor
jak to zrobić na PHP 5x?
@YYYumYum sprawdź 5-krotnie odpowiedź dbau, która korzysta z tej techniki php.net/manual/en/function.stream-context-create.php Lub zawsze możesz wrócić do standardowego rozwiązania curl.
Imran Zahoor,
5

Jest jeszcze jeden, z którego możesz skorzystać

<?php
$fields = array(
    'name' => 'mike',
    'pass' => 'se_ret'
);
$files = array(
    array(
        'name' => 'uimg',
        'type' => 'image/jpeg',
        'file' => './profile.jpg',
    )
);

$response = http_post_fields("http://www.example.com/", $fields, $files);
?>

Kliknij tutaj po szczegóły

Kod
źródło
2
Zależy to od rozszerzenia PECL, którego większość nie zainstaluje. Nie jestem nawet pewien, czy nadal jest dostępny, ponieważ strony podręcznika zostały usunięte.
miken32
5

Szukałem podobnego problemu i znalazłem lepsze podejście do tego. Więc proszę.

Możesz po prostu umieścić następujący wiersz na stronie przekierowania (powiedz page1.php).

header("Location: URL", TRUE, 307); // Replace URL with to be redirected URL, e.g. final.php

Potrzebuję tego, aby przekierować żądania POST dla wywołań interfejsu API REST . To rozwiązanie może przekierowywać dane postu oraz niestandardowe wartości nagłówka.

Oto link referencyjny .

Arindam Nayak
źródło
1
To odpowiada, jak przekierować żądanie strony nie Jak wysłać żądanie POST za pomocą PHP? Jasne, że przekazałoby to wszelkie parametry POST, ale to wcale nie jest to samo
Wesley Smith,
@ DelightedD0D, Niestety nie miałem różnicę między redirect a page request with POST paramvs send POST request. Dla mnie cel obu jest taki sam, popraw mnie, jeśli się mylę.
Arindam Nayak
1
Czy jest jakaś metoda, która pozwoli mi wysłać parametry metodą POST, a następnie odczytać zawartość za pośrednictwem PHP? OP chce, aby ich skrypt php skonstruował zestaw parametrów POST i wysłał je na inną stronę php, aby ich skrypt otrzymał dane wyjściowe z tej strony. To rozwiązanie po prostu zaakceptuje zestaw wartości POSTed i przekaże je na inną stronę. Oni są zupełnie inni.
Wesley Smith
5

Tutaj używa się tylko jednego polecenia bez cURL. Super proste.

echo file_get_contents('https://www.server.com', false, stream_context_create([
    'http' => [
        'method' => 'POST',
        'header'  => "Content-type: application/x-www-form-urlencoded",
        'content' => http_build_query([
            'key1' => 'Hello world!', 'key2' => 'second value'
        ])
    ]
]));
Liga
źródło
Jak działa Key2? jaki jest między nimi separator?
Sayed Muhammad Idrees
@ Sayedidrees, aby dodać key2, możesz wprowadzić go jako drugi element tablicy. „key1” => „Witaj świecie!”, „key2” => „druga wartość”
Liga
Działa to bardzo dobrze podczas korzystania z zapiera.
Moxet,
3

Wypróbuj pakiet PEAR HTTP_Request2 , aby łatwo wysyłać żądania POST. Alternatywnie możesz użyć funkcji curl PHP lub kontekstu strumienia PHP .

HTTP_Request2 umożliwia także wykpienie serwera , dzięki czemu można łatwo przetestować kod jednostkowo

cweiske
źródło
7
Jeśli to możliwe, chciałbym cię zobaczyć.
Gui Imamura,