HTTP request & response
HTTP požadavek a odpověď se zapouzdřuje do objektů Nette\Http\Request a Response, které nabízejí přehledné API a fungují zároveň jako sanitizační filtr.
Instalace:
composer require nette/http
HTTP požadavek
HTTP požadavek představuje objekt Nette\Http\Request, ke
kterému se dostanete tak, že si jej necháte předat pomocí dependency injection.
V presenterech stačí jen zavolat $httpRequest = $this->getHttpRequest()
.
Co je důležité, tak že Nette když vytváří tento objekt, všechny vstupní parametry GET, POST, COOKIE a také URL pročistí od kontrolních znaků a neplatných UTF-8 sekvencí. Takže s daty pak můžete bezpečně dále pracovat. Očištěná data se následně používají v presenterech a formulářích.
Třída Request
je immutable (neměnná). Nemá žádné settery, má jen jeden tzv. wither
withUrl()
, který objekt nemění, ale vrací novou instanci se změněnou hodnotou.
withUrl(Nette\Http\UrlScript $url): Nette\Http\Request
Vrací klon s jinou URL.
getUrl(): Nette\Http\UrlScript
Vrací URL požadavku jako objekt UrlScript.
$url = $httpRequest->getUrl();
echo $url; // https://doc.nette.org/cs/?action=edit
echo $url->getHost(); // nette.org
Prohlížeče neodesílají na server fragment, takže $url->getFragment()
bude vracet prázdný řetězec.
getQuery(string $key = null): string|array|null
Vrací parametry GET požadavku.
$all = $httpRequest->getQuery(); // vrací pole všech parametrů z URL
$id = $httpRequest->getQuery('id'); // vrací GET parametr 'id' (nebo null)
getPost(string $key = null): string|array|null
Vrací parametry POST požadavku.
$all = $httpRequest->getPost(); // vrací pole všech parametrů z POST
$id = $httpRequest->getPost('id'); // vrací POST parametr 'id' (nebo null)
getFile(string $key): Nette\Http\FileUpload|array|null
Vrací upload jako objekt Nette\Http\FileUpload:
$file = $httpRequest->getFile('avatar');
if ($file->hasFile()) { // byl nějaký soubor nahraný?
$file->getName(); // jméno souboru odeslané uživatelem
$file->getSanitizedName(); // jméno bez nebezpečných znaků
}
getFiles(): array
Vrátí strom všech uploadů v normalizované struktuře, jejíž listy jsou objekty Nette\Http\FileUpload:
$files = $httpRequest->getFiles();
getCookie(string $key): string|array|null
Vrací cookie nebo null
, když neexistuje.
$sessId = $httpRequest->getCookie('sess_id');
getCookies(): array
Vrací všechny cookies.
$cookies = $httpRequest->getCookies();
getMethod(): string
Vrací HTTP metodu, se kterou byl udělán požadavek.
$httpRequest->getMethod(); // GET, POST, HEAD, PUT
isMethod(string $method): bool
Testuje HTTP metodu, se kterou byl udělán požadavek. Parametr je case-insensitive.
if ($httpRequest->isMethod('GET')) ...
getHeader(string $header): ?string
Vrací HTTP hlavičku nebo null
, pokud neexistuje. Parametr je case-insensitive.
$userAgent = $httpRequest->getHeader('User-Agent');
getHeaders(): array
Vrací všechny HTTP hlavičky jako asociativní pole.
$headers = $httpRequest->getHeaders();
echo $headers['Content-Type'];
getReferer(): ?Nette\Http\UrlImmutable
Z jaké URL uživatel přišel? Pozor, není vůbec spolehlivé.
isSecured(): bool
Je spojení šifrované (HTTPS)? Pro správnou funkčnost může být potřeba nastavit proxy.
isSameSite(): bool
Přichází požadavek ze stejné (sub)domény a je iniciován kliknutím na odkaz? Nette k detekci používá cookie
nette-samesite
.
isAjax(): bool
Jde o AJAXový požadavek?
getRemoteAddress(): ?string
Vrací IP adresu uživatele. Pro správnou funkčnost může být potřeba nastavit proxy.
getRemoteHost(): ?string
Vrací DNS překlad IP adresy uživatele. Pro správnou funkčnost může být potřeba nastavit proxy.
getRawBody(): ?string
Vrací tělo HTTP požadavku.
$body = $httpRequest->getRawBody();
detectLanguage(array $langs): ?string
Detekuje jazyk. Jako parametr $lang
předáme pole s jazyky, které aplikace podporuje, a ona vrátí ten, který
by viděl návštěvníkův prohlížeč nejraději. Nejsou to žádná kouzla, jen se využívá hlavičky
Accept-Language
. Pokud nedojde k žádné shodě, vrací null
.
// prohlížeč odesílá např. Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3
$langs = ['hu', 'pl', 'en']; // jazyky podporované aplikací
echo $httpRequest->detectLanguage($langs); // en
RequestFactory
Objekt aktuálního HTTP requestu vyrobí Nette\Http\RequestFactory. Pokud píšete aplikaci, která nepoužívá DI kontejner, vyrobíte request takto:
$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();
RequestFactory lze před zavoláním fromGlobals()
konfigurovat. Můžeme vypnout sanitizaci vstupních parametrů
od kontrolních znaků a neplatných UTF-8 sekvencí pomocí $factory->setBinary()
. A také nastavit proxy
server pomocí $factory->setProxy(...)
, což je důležité pro správnou detekci IP adresy uživatele.
Pomocí tzv. filtrů lze URL vyčistit od znaků, které se do něj mohou dostat např. kvůli špatně implementovaným komentářovým systémům na různých cizích webech:
// odstraníme mezery z cesty
$requestFactory->urlFilters['path']['%20'] = '';
// odstraníme tečku, čárku nebo pravou závorku z konce URI
$requestFactory->urlFilters['url']['[.,)]$'] = '';
// vyčistíme cestu od zdvojených lomítek (výchozí filtr)
$requestFactory->urlFilters['path']['/{2,}'] = '/';
HTTP odpověď
HTTP odpověď představuje objekt Nette\Http\Response, ke
kterému se dostanete tak, že si jej necháte předat pomocí dependency injection.
V presenterech stačí jen zavolat $httpResponse = $this->getHttpResponse()
.
Objekt je na rozdíl od Request mutable, tedy pomocí setterů můžete měnit stav, tedy
např. odesílat hlavičky. Nezapomeňte, že všechny settery musí být volány před odesláním jakéhokoli výstupu.
Jestli už byl výstup odeslán prozradí metoda isSent()
. Pokud vrací true
, každý pokus
o odeslání hlavičky vyvolá výjimku Nette\InvalidStateException
.
setCode(int $code, string $reason = null)
Změní stavový kód odpovědi. Kvůli lepší srozumitelnosti zdrojového kódu doporučujeme pro kód používat místo čísel předdefinované konstanty.
$httpResponse->setCode(Nette\Http\Response::S404_NOT_FOUND);
getCode(): int
Vrací stavový kód odpovědi.
isSent(): bool
Vrací, zda už došlo k odeslání hlaviček ze serveru do prohlížeče, a tedy již není možné odesílat hlavičky či měnit stavový kód.
setHeader(string $name, string $value)
Odešle HTTP hlavičku a přepíše dříve odeslanou hlavičkou stejného jména.
$httpResponse->setHeader('Pragma', 'no-cache');
addHeader(string $name, string $value)
Odešle HTTP hlavičku a nepřepíše dříve odeslanou hlavičkou stejného jména.
$httpResponse->addHeader('Accept', 'application/json');
$httpResponse->addHeader('Accept', 'application/xml');
deleteHeader(string $name)
Smaže dříve odeslanou HTTP hlavičku.
getHeader(string $header): ?string
Vrací odeslanou HTTP hlavičku nebo null
, pokud taková neexistuje. Parametr je case-insensitive.
$pragma = $httpResponse->getHeader('Pragma');
getHeaders(): array
Vrací všechny odeslané HTTP hlavičky jako asociativní pole.
$headers = $httpResponse->getHeaders();
echo $headers['Pragma'];
setContentType(string $type, string $charset = null)
Mění hlavičku Content-Type
.
$httpResponse->setContentType('text/plain', 'UTF-8');
redirect(string $url, int $code = self::S302_FOUND): void
Přesměruje na jiné URL. Nezapomeňte poté ukončit skript.
$httpResponse->redirect('http://example.com');
exit;
setExpiration(?string $time)
Nastaví expiraci HTTP dokumentu pomocí hlaviček Cache-Control
a Expires
. Parametrem je buď
časový interval (jako text), nebo null
, což zakáže kešování.
// cache v prohlížeči vyprší za hodinu
$httpResponse->setExpiration('1 hour');
setCookie(string $name, string $value, $time, string $path = null, string $domain = null, bool $secure = null, bool $httpOnly = null, string $sameSite = null)
Odešle cookie. U neuvedených parametrů nebo při uvedení null se použijí tyto výchozí hodnoty:
$path = '/'
, tedy cookie má dosah na všechny cesty v (sub)doméně$domain
není nastaveno, což znamená s dosahem na aktuální (sub)doménu, ale nikoliv její subdomény$secure
se řídí podle nastavení v konfiguraci$httpOnly = true
, tedy cookie je pro JavaScript nepřístupná$sameSite = null
, tedy flag se neodesílá (viz SameSite cookie)
Čas lze uvádět jako řetězec nebo počet sekund.
$httpResponse->setCookie('lang', 'cs', '100 days');
deleteCookie(string $name, string $path = null, string $domain = null, bool $secure = null): void
Smaže cookie. Výchozí hodnoty parametrů jsou:
$path
s dosahem na všechny adresáře ('/'
)$domain
s dosahem na aktuální (sub)doménu, ale nikoliv její subdomény$secure
se řídí podle nastavení v konfiguraci
$httpResponse->deleteCookie('lang');
Uploadované soubory
Metoda Nette\Http\Request::getFiles()
vrací pole všech uploadů v normalizované struktuře, jejíž listy jsou
objekty Nette\Http\FileUpload. Ty zapouzdřují data odeslaná
formulářovým prvkem <input type=file>
.
Struktura reflektuje pojmenování prvků v HTML. V nejjednodušším případě to může být jediný pojmenovaný element formuláře odeslaný jako:
<input type="file" name="avatar">
V tomto případě $request->getFiles()
vrací pole:
[
'avatar' => /* FileUpload instance */
]
Objekt FileUpload
se vytvoří i v případě, že uživatel žádný soubor neodeslal nebo odeslání selhalo.
Jestli byl soubor odeslán vrací metoda hasFile()
:
$request->getFile('avatar')->hasFile();
V případě názvu elementu používajícího notaci pro pole:
<input type="file" name="my-form[details][avatar]">
vypadá vrácený strom takto:
[
'my-form' => [
'details' => [
'avatar' => /* FileUpload instance */
],
],
]
Lze vytvořit i pole souborů:
<input type="file" name="my-form[details][avatars][]" multiple>
V takovém případě vypadá struktura takto:
[
'my-form' => [
'details' => [
'avatars' => [
0 => /* FileUpload instance */,
1 => /* FileUpload instance */,
2 => /* FileUpload instance */,
],
],
],
]
Přistoupit k indexu 1 vnořeného pole lze nejlépe takto:
$file = Nette\Utils\Arrays::get(
$request->getFiles(),
['my-form', 'details', 'avatars', 1],
null
);
if ($file instanceof FileUpload) {
...
}
Protože nelze důvěřovat datům zvenčí a tedy ani spoléhat na podobu struktury souborů, je bezpečnější použít
metodu Arrays::get() než třeba výraz
$request->getFiles()['my-form']['details']['avatars'][1]
, který může selhat.
Přehled metod FileUpload
hasFile(): bool
Vrací true
, pokud uživatel nějaký soubor uploadoval.
isOk(): bool
Vrací true
, pokud soubor byl nahrán úspěšně.
getError(): int
Vrací kód chyby při uploadu souboru. Jde o jednu z konstant UPLOAD_ERR_XXX. V případě, že upload proběhl
v pořádku, vrací UPLOAD_ERR_OK
.
move(string $dest)
Přesune nahraný soubor do nového umístění. Pokud cílový soubor již existuje, bude přepsán.
$file->move('/path/to/files/name.ext');
getContents(): ?string
Vrací obsah uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací null
.
getContentType(): ?string
Detekuje MIME content type uploadovaného souboru na základě jeho signatury. V případě, že upload nebyl úspěšný nebo
detekce se nezdařila, vrací null
.
Vyžaduje PHP rozšíření fileinfo
.
getName(): string
Vrací originální název souboru, jak jej odeslal prohlížeč.
Nevěřte hodnotě vrácené touto metodou. Klient mohl odeslat škodlivý název souboru s úmyslem poškodit nebo hacknout vaši aplikaci.
getUntrustedName(): string
Alias pro getName()
.
getSanitizedName(): string
Vrací sanitizovaný název souboru. Obsahuje pouze ASCII znaky [a-zA-Z0-9.-]
.
Od verze 3.0.5: Pokud název takové znaky neobsahuje, vrátí 'unknown'
. Pokud je soubor obrázek ve formátu
JPEG, PNG, GIF, nebo WebP, vrátí i správnou příponu.
getSize(): int
Vrací velikost uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací 0
.
getTemporaryFile(): string
Vrací cestu k dočasné lokaci uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací
''
.
isImage(): bool
Vrací true
, pokud nahraný soubor je obrázek ve formátu JPEG, PNG, GIF, nebo WebP. Detekce probíhá na
základě jeho signatury a neověřuje se integrita celého souboru. Zda není obrázek poškozený lze zjistit například
pokusem o jeho načtení.
Vyžaduje PHP rozšíření fileinfo
.
getImageSize(): ?array
Vrací dvojici [šířka, výška]
s rozměry uploadovaného obrázku. V případě, že upload nebyl
úspěšný nebo nejde o platný obrázek, vrací null
.
getImageFileExtension(): ?string
Vrátí příponu souboru obrázku na základě zjištěného typu obsahu (bez tečky).
toImage(): Nette\Utils\Image
Načte obrázek jak objekt Image. V případě, že upload nebyl úspěšný nebo nejde
o platný obrázek, vyhodí výjimku Nette\Utils\ImageException
.
Související články na blogu
Jak správně nastavit CSP a script-src
Content Security Policy (CSP) je dodatečný bezpečnostní prvek, který prohlížeči říká, jaké další zdroje může stránka načítat a jak může být zobrazena…
HTTP požadavky a odpovědi – Část 3.
V první a druhé části této minisérie popisuji možnosti ovládání HTTP protokolu z presenteru Nette aplikace. V tomto dílu se zaměřuji na nástroje…
HTTP požadavky a odpovědi – Část 2.
V první části popisuji metody presenteru, kterými můžeme ovládat HTTP odpovědi aplikace. V této části se budu věnovat rozhaní Nette\Application…
HTTP požadavky a odpovědi – Část 1.
Nette nabízí pro práci s HTTP dvě vrstvy abstrakce. První, nízkoúrovňovou, obstarávají třídy ze jmenného prostoru Nette\Http. Nabízejí příjemné API…