cgi
— wsparcie dla wspólnego sprzęgu bramki - z ang. - Common Gateway Interface¶
Source code: Lib/cgi.py
Deprecated since version 3.11, will be removed in version 3.13: The cgi
module is deprecated
(see PEP 594 for details and alternatives).
The FieldStorage
class can typically be replaced with
urllib.parse.parse_qsl()
for GET
and HEAD
requests,
and the email.message
module or
multipart for POST
and PUT
.
Most utility functions have replacements.
Wspierający moduł dla skryptów sprzęgu wspólnej bramki (z ang. CGI).
Ten moduł definiuje pewną liczbę użyteczności dla użycia przez skrypty CGI napisane w języku pytonowskim.
The global variable maxlen
can be set to an integer indicating the maximum
size of a POST request. POST requests larger than this size will result in a
ValueError
being raised during parsing. The default value of this
variable is 0
, meaning the request size is unlimited.
Availability: not Emscripten, not WASI.
This module does not work or is not available on WebAssembly platforms
wasm32-emscripten
and wasm32-wasi
. See
WebAssembly platforms for more information.
Wprowadzenie¶
Skrypt CGI jest wywoływany przez serwer HTTP, zwykle aby przetworzyć wejście użytkownika dostarczone przez HTML <FORM>
lub element <ISINDEX>
.
Najczęściej, Skrypty CGI przebywają w katalogu specjalnym serwera cgi-bin
. Serwer HTTP umieszcza wszelkiego rodzaju informacje o zapytaniu (takie jak nazwa hosta klienta, oczekiwany adres URL, ciąg zapytania, i wiele innych dobrych rzeczy) w środowisku powłoki skryptu, wykonuje skrypt i wysyła wyjście skryptu z powrotem do klienta.
Wejście skryptu jest połączone z klientem także, i czasami dane formularza są wczytywane tą drogą także; innym razem dane formularza są przekazywane przez „ciąg zapytania” jako część adresu URL. Ten moduł jest przeznaczony do zajmowania się różnymi przypadkami i dostarczenia prostszego sprzęgu dla skryptu języka pytonowskiego. To także dostarcza pewną liczbę użyteczności które pomagają w odpluskwianiu skryptów zaś najnowszym dodatkiem jest wsparcie dla załadowywania plików przez formularz (jeśli twoja przeglądarka to wspomaga).
Wyjście skryptu CGI powinno składać się z dwóch sekcji, oddzielonych pustą linią. Pierwszy rozdział zawiera liczbę nagłówków, mówiąc klientowi jakiego rodzaju dane nastąpią potem. Kod języka pytonowskiego aby wytworzyć minimalny rozdział nagłówkowy wygląda następująco:
print("Content-Type: text/html") # HTML is following
print() # blank line, end of headers
Drugi rozdziałem jest zwykle HTML, który pozwala oprogramowaniu klienta wyświetlić ładnie sformatowany tekst z nagłówkiem, obrazkami w-jednej-linii itp. Tu jest kod języka pytonowskiego który drukuje prosty kawałek HTML-a:
print("<TITLE>CGI script output</TITLE>")
print("<H1>This is my first CGI script</H1>")
print("Hello, world!")
Użycie modułu cgi¶
Zacznij przez napisanie import cgi
.
Gdy piszesz nowy skrypt, rozważ dodanie tych linii:
import cgitb
cgitb.enable()
This activates a special exception handler that will display detailed reports in the web browser if any errors occur. If you’d rather not show the guts of your program to users of your script, you can have the reports saved to files instead, with code like this:
import cgitb
cgitb.enable(display=0, logdir="/path/to/logdir")
Jest bardzo przydatnym użycie tej właściwości w czasie rozwijania skryptu. Raporty produkowane przez cgitb
dostarczają informację która może oszczędzić Ci wiele czasu w śledzeniu błędów. Możesz zawsze usunąć cgitb
linię później gdy przetestowałeś swój skrypt i jesteś pewien że działa on prawidłowo.
To get at submitted form data, use the FieldStorage
class. If the form
contains non-ASCII characters, use the encoding keyword parameter set to the
value of the encoding defined for the document. It is usually contained in the
META tag in the HEAD section of the HTML document or by the
Content-Type header. This reads the form contents from the
standard input or the environment (depending on the value of various
environment variables set according to the CGI standard). Since it may consume
standard input, it should be instantiated only once.
The FieldStorage
instance can be indexed like a Python dictionary.
It allows membership testing with the in
operator, and also supports
the standard dictionary method keys()
and the built-in function
len()
. Form fields containing empty strings are ignored and do not appear
in the dictionary; to keep such values, provide a true value for the optional
keep_blank_values keyword parameter when creating the FieldStorage
instance.
Dla przykładu, następujący kod (który zakłada, że nagłówek Content-Type i puste linie zostały już wydrukowane) sprawdza czy pola nazwa
i addr
zostały oba ustawione na nie-pusty ciąg znaków:
form = cgi.FieldStorage()
if "name" not in form or "addr" not in form:
print("<H1>Error</H1>")
print("Please fill in the name and addr fields.")
return
print("<p>name:", form["name"].value)
print("<p>addr:", form["addr"].value)
...further form processing here...
Here the fields, accessed through form[key]
, are themselves instances of
FieldStorage
(or MiniFieldStorage
, depending on the form
encoding). The value
attribute of the instance yields
the string value of the field. The getvalue()
method
returns this string value directly; it also accepts an optional second argument
as a default to return if the requested key is not present.
If the submitted form data contains more than one field with the same name, the
object retrieved by form[key]
is not a FieldStorage
or
MiniFieldStorage
instance but a list of such instances. Similarly, in
this situation, form.getvalue(key)
would return a list of strings. If you
expect this possibility (when your HTML form contains multiple fields with the
same name), use the getlist()
method, which always returns
a list of values (so that you do not need to special-case the single item
case). For example, this code concatenates any number of username fields,
separated by commas:
value = form.getlist("username")
usernames = ",".join(value)
If a field represents an uploaded file, accessing the value via the
value
attribute or the getvalue()
method reads the entire file in memory as bytes. This may not be what you
want. You can test for an uploaded file by testing either the
filename
attribute or the file
attribute. You can then read the data from the file
attribute before it is automatically closed as part of the garbage collection of
the FieldStorage
instance
(the read()
and readline()
methods will
return bytes):
fileitem = form["userfile"]
if fileitem.file:
# It's an uploaded file; count lines
linecount = 0
while True:
line = fileitem.file.readline()
if not line: break
linecount = linecount + 1
FieldStorage
objects also support being used in a with
statement, which will automatically close them when done.
If an error is encountered when obtaining the contents of an uploaded file
(for example, when the user interrupts the form submission by clicking on
a Back or Cancel button) the done
attribute of the
object for the field will be set to the value -1.
Standard ładowania pliku przewiduje możliwość załadowania wielu plików z jednego pola (używając rekursywnego kodowania multipart/*). Gdy to się zdarzy, element będzie słowniko-podobnym elementem FieldStorage
. To może być określone przez sprawdzenie właściwości type
, która powinna być multipart/form-data (lub byćmoże inny typ MIME pasujący do multipart/*). W tym przypadku może być on iterowany rekursywnie tak, jak przedmiot formularza nadrzędnego poziomu.
Gdy formularz jest podawany w „starym” formacie (jako ciąg zapytania lub pojedyncza część danych typu application/x-www-form-urlencoded), wszystkie elementy właściwie będą przykładami uogólnienia MiniFieldStorage
. W tym przypadku, właściwości list
, file
, i filename
są zawsze None
.
Formularz podany przez sposób postępowania POST który także zawiera ciąg zapytania będzie zawierał zarówno elementy FieldStorage
jak i MiniFieldStorage
.
Zmienione w wersji 3.4: The file
attribute is automatically closed upon the
garbage collection of the creating FieldStorage
instance.
Zmienione w wersji 3.5: Added support for the context management protocol to the
FieldStorage
class.
Sprzęg Wyższego Poziomu (Higher Level Interface)¶
Poprzednia sekcja wyjaśnia jak czytać dane z formularza CGI używając uogólnienia FieldStorage
. Ten rozdział opisuje sprzęg wyższego rzędu, który został dodany do tego uogólnienia aby pozwolić robić to w bardziej czytelny i intuicyjny sposób. Sprzęg nie powoduje że techniki opisane w poprzednich rozdziałach stają się zbędne — one są wciąż użyteczne aby wykonywać ładowanie plików efektywnie, dla przykładu.
Sprzęg składa się z dwóch prostych sposobów postępowania. Używając sposobów postępowania możesz przetwarzać dane w zastępczy sposób bez konieczności martwienia się czy tylko jedna czy więcej danych zostało opublikowanych pod daną nazwą.
W poprzednim rozdziale, dowiedziałeś się jak pisać następujący kod za każdym razem gdy spodziewałeś się że użytkownik opublikuje więcej niż jedną wartość pod jedną nazwą:
item = form.getvalue("item")
if isinstance(item, list):
# The user is requesting more than one item.
else:
# The user is requesting only one item.
Ta sytuacja jest obecna dla przykładu gdy formularz zawiera grupę wielu pól zaznaczania o tej samej nazwie:
<input type="checkbox" name="item" value="1" />
<input type="checkbox" name="item" value="2" />
W większości przypadków, jednakże, istnieje tylko jedna urządzenie formularza o określonej nazwie w formularzu i wtedy oczekujesz i potrzebujesz tylko jednej wartości powiązanej z tą nazwą. Więc piszesz skrypt zawierający dla przykładu ten kod:
user = form.getvalue("user").upper()
Problem z kodem polega na tym, że nigdy nie powinieneś oczekiwać, że klient dostarczy właściwe wpisy do twoich skryptów. Dla przykładu, jeśli ciekawy użytkownik dołoży jeszcze jedną parę user=foo
do ciągu zapytania, wtedy skrypt ulegnie wypadkowi, ponieważ w tej sytuacji wywołanie sposobu postępowania getvalue("user")
zwróci listę zamiast ciągu znaków. Wywołanie sposobu postępowania upper()
na liście nie jest prawidłowe (gdyż listy nie mają sposobów postępowania o tej nazwie) i kończy się wyjątkiem AttributeError
.
Zatem, właściwym sposobem aby wczytywać dane formularza było zawsze używanie kodu który sprawdza czy otrzymana wartość jest pojedynczą wartością czy listą wartości. To jest irytujące i prowadzi do mniej czytelnych skryptów.
A more convenient approach is to use the methods getfirst()
and getlist()
provided by this higher level interface.
- FieldStorage.getfirst(name, default=None)¶
Ten sposób postępowania zawsze zwraca tylko jedną wartość związaną z polem formularza name. Sposób postępowania zwraca tylko pierwszą wartość w przypadku gdy więcej wartości zostało opublikowanych pod taką nazwą. Proszę zauważ że kolejność w jakiej wartości są otrzymywane może różnić się między przeglądarkami i nie powinien liczyć się. [1] Jeśli żadne takie pole formularza ani wartość nie istnieje wtedy sposób postępowania zwraca wartość określoną przez opcjonalny parametr domyślny - z ang - default. Ten parametr domyślnie równy jest
None
jeśli nie jest określony.
- FieldStorage.getlist(name)¶
Ten sposób postępowania zawsze zwraca listę wartości związaną z polem formularza name. Ten sposób postępowania zwraca pustą listę jeśli żadne pole ani wartość nie istnieje dla nazwy - z ang. - name. Zwraca listę składającą się z jednego elementu jeśli tylko jedna taka wartość istnieje.
Używając tych sposobów postępowania możesz napisać ładny zgrabny kod:
import cgi
form = cgi.FieldStorage()
user = form.getfirst("user", "").upper() # This way it's safe.
for item in form.getlist("item"):
do_something(item)
Zadania¶
Te są użyteczne jeśli chcesz więcej kontrolować, lub jeśli chcesz zatrudnić niektóre z algorytmów wypełnionych w tym module w innych przypadkach.
- cgi.parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator='&')¶
Parse a query in the environment or from a file (the file defaults to
sys.stdin
). The keep_blank_values, strict_parsing and separator parameters are passed tourllib.parse.parse_qs()
unchanged.Deprecated since version 3.11, will be removed in version 3.13: This function, like the rest of the
cgi
module, is deprecated. It can be replaced by callingurllib.parse.parse_qs()
directly on the desired query string (except formultipart/form-data
input, which can be handled as described forparse_multipart()
).
- cgi.parse_multipart(fp, pdict, encoding='utf-8', errors='replace', separator='&')¶
Parse input of type multipart/form-data (for file uploads). Arguments are fp for the input file, pdict for a dictionary containing other parameters in the Content-Type header, and encoding, the request encoding.
Returns a dictionary just like
urllib.parse.parse_qs()
: keys are the field names, each value is a list of values for that field. For non-file fields, the value is a list of strings.This is easy to use but not much good if you are expecting megabytes to be uploaded — in that case, use the
FieldStorage
class instead which is much more flexible.Zmienione w wersji 3.7: Added the encoding and errors parameters. For non-file fields, the value is now a list of strings, not bytes.
Zmienione w wersji 3.10: Added the separator parameter.
Deprecated since version 3.11, will be removed in version 3.13: This function, like the rest of the
cgi
module, is deprecated. It can be replaced with the functionality in theemail
package (e.g.email.message.EmailMessage
/email.message.Message
) which implements the same MIME RFCs, or with the multipart PyPI project.
- cgi.parse_header(string)¶
Wczytaj nagłówek MIME (taki, jak Content-Type) do głównej wartości i słownika parametrów.
Deprecated since version 3.11, will be removed in version 3.13: This function, like the rest of the
cgi
module, is deprecated. It can be replaced with the functionality in theemail
package, which implements the same MIME RFCs.For example, with
email.message.EmailMessage
:from email.message import EmailMessage msg = EmailMessage() msg['content-type'] = 'application/json; charset="utf8"' main, params = msg.get_content_type(), msg['content-type'].params
- cgi.test()¶
Robust test CGI script, usable as main program. Writes minimal HTTP headers and formats all information provided to the script in HTML format.
- cgi.print_environ()¶
Formatuje środowisko powłoki w HTML-u
- cgi.print_form(form)¶
Formatuje formularz w HTML-u.
- cgi.print_directory()¶
Formatuje obecny katalog w HTML-u.
- cgi.print_environ_usage()¶
Drukuje listę użytecznych (używanych przez CGI) zmiennych środowiskowych w HTML-u.
Troska o bezpieczeństwo¶
There’s one important rule: if you invoke an external program (via
os.system()
, os.popen()
or other functions with similar
functionality), make very sure you don’t pass arbitrary strings received from
the client to the shell. This is a well-known security hole whereby clever
hackers anywhere on the web can exploit a gullible CGI script to invoke
arbitrary shell commands. Even parts of the URL or field names cannot be
trusted, since the request doesn’t have to come from your form!
Aby trzymać się bezpiecznej strony, jeśli przekazujesz ciąg znaków otrzymany z formularza do polecenia powłoki, powinieneś upewnić się, że ciąg znaków zawiera tylko znaki alfanumeryczne, ukośniki, podkreślenia i kropki.
Instalowanie twojego skryptu CGI na systemie Unix-owym¶
Przeczytaj dokumentację dla twojego serwera HTTP i sprawdź u swojego administratora systemu aby dowiedzieć się który katalog powinien być użyty dla instalowania skryptów CGI; zwykle to jest katalog cgi-bin
w drzewie serwera.
Upewnij się że twój skrypt jest czytelny i wykonywalny przez „innych”; tryb pliku Unix-a powinien być 0o755
ósemkowo (użyj chmod 0755 nazwa-pliku
). Upewnij się, że pierwsza linia skryptu zawiera #!
zaczynając w kolumnie 1 po której następuje ścieżka dostępu do pliku programu interpretującego polecenia języka pytonowskiego, dla przykładu:
#!/usr/local/bin/python
Upewnij się że program interpretujący polecenia języka pytonowskiego istnieje i jest wykonywalny przez „innych”.
Upewnij się, że jakiekolwiek pliki które twój skrypt chce odczytać lub zapisać są czytelne i możliwe do zapisania odpowiednio, przez „innych” — ich tryb powinien być 0o644
dla czytelnych i 0o666
dla możliwych do zapisania. To jest ponieważ z powodów bezpieczeństwa, serwer HTTP wykonuje twój skrypt jako użytkownik „nikt” - z ang. - „nobody” bez żadnych szczególnych przywilejów. Może tylko wczytywać (zapisywać, wykonywać) pliki które wszyscy mogą czytać (zapisać, wykonać). Bierzący katalog w czasie wykonania jest także inny (jest to zwykle katalog serwera cgi-bin) i ustawienie zmiennych środowiskowych jest także inne od tego które dostajesz gdy się zalogujesz. W szczególności, nie licz na ścieżkę przeszukiwania pod kątem plików wykonywalnych (PATH
) ani na ścieżkę przeszukiwania modułów języka pytonowskiego (PYTHONPATH
), że będą ustawione na cokolwiek interesującego.
Jeśli potrzebujesz załadować moduły z katalogu który nie jest domyślną ścieżką przeszukiwania modułów języka pytonowskiego, możesz zmienić ścieżkę w twoim skrypcie, przez importowaniem innych modułów. Dla przykładu:
import sys
sys.path.insert(0, "/usr/home/joe/lib/python")
sys.path.insert(0, "/usr/local/lib/python")
(W ten sposób, katalog wstawiony jako ostatni będzie przeszukiwany jako pierwszy!)
Instrukcje dla nie-Unixowych systemów będą różne; Sprawdź dokumentację serwera HTTP (będzie zwykle miała sekcję o skryptach CGI).
Sprawdzanie twoich skryptów CGI¶
Niestety, skrypt CGI zwykle nie uruchomi się gdy spróbujesz go uruchomić z wiersza poleceń i skrypt który działa dobrze z wiersza polecenia może zawieźć nieoczekiwanie gdy uruchomiony z serwera. Jest jeden powód dla którego wciąż powinieneś testować swój skrypt z wiersza polecenia: jeśli zawiera błąd składniowy, program interpretujący polecenia języka pytonowskiego nie wykona go w ogóle, a serwer HTTP najprawdopodobniej wyśle tajemniczy komunikat o błędzie do klienta.
Zakładając, że twój skrypt nie ma błędów składniowych, a jednak wciąż nie działa, nie masz wyboru, tylko musisz czytać dalej.
Odpluskwianie skryptów CGI¶
First of all, check for trivial installation errors — reading the section
above on installing your CGI script carefully can save you a lot of time. If
you wonder whether you have understood the installation procedure correctly, try
installing a copy of this module file (cgi.py
) as a CGI script. When
invoked as a script, the file will dump its environment and the contents of the
form in HTML format. Give it the right mode etc., and send it a request. If it’s
installed in the standard cgi-bin
directory, it should be possible to
send it a request by entering a URL into your browser of the form:
http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home
Jeśli zwróci błąd typu 404, wtedy serwer nie może znaleźć skryptu – być może potrzebujesz zainstalować go w innych katalogu. Jeśli daje inny błąd, istnieje problem instalacji który powinieneś naprawić zanim spróbujesz iść dalej. Jeśli otrzymujesz ładnie sformatowany wypis środowiska i zawartości formularza (w tym przypadku, pola powinny być wypisane jako „addr” z wartością „At Home” i „name” z wartością „Joe Blow”), plik skryptu cgi.py
zostały zainstalowane poprawnie. Jeśli wykonasz tę samą procedurę dla twojego własnego skryptu, powinieneś móc go teraz odpluskwiać.
Następnym krokiem mogłoby być wezwanie zadania test()
modułu cgi
z twojego skryptu: zamiana jej głównego kodu na pojedyncze stwierdzenie:
cgi.test()
To powinno dać te same wyniki jak te otrzymane z zainstalowania samego pliku cgi.py
.
Gdy zwykły skrypt języka pytonowskiego zgłasza nieobsłużony wyjątek (dla jakiegokolwiek powodu: z powodu literówki w nazwie modułu, pliku który nie może być otwarty, itp.), program interpretujący polecenia języka pytonowskiego wypisuje ładny wypis i wychodzi. Podczas gdy program interpretujący polecenia języka pytonowskiego będzie wciąż to robił gdy twój skrypt CGI zgłosi wyjątek, najprawdopodobniej wypis skończy w jednym z plików logów serwera HTTP, lub zostanie odrzucony całkowicie.
Fortunately, once you have managed to get your script to execute some code,
you can easily send tracebacks to the web browser using the cgitb
module.
If you haven’t done so already, just add the lines:
import cgitb
cgitb.enable()
na górę twojego skryptu. Wtedy spróbuj uruchomić go jeszcze raz; gdy problem pojawi się znów, powinieneś zobaczyć szczegółowy raport, który prawdopodobnie uczyni jasnym powód wypadku.
Jeśli podejrzewasz, że może być problem w importowaniu modułu cgitb
, możesz użyć nawet jeszcze bardziej wydajnego podejścia (które używa tylko modułów wbudowanych):
import sys
sys.stderr = sys.stdout
print("Content-Type: text/plain")
print()
...your code here...
To opiera się na programie interpretującym polecenia języka pytonowskiego, że wydrukuje swój wypis. Typ treści wyjścia jest ustawiony na zwykły tekst, co wyklucza całą obsługę HTML. Jeśli twój skrypt działa, surowy HTML zostanie pokazany przez twojego klienta. Jeśli zgłasza wyjątek, najprawdopodobniej po tym jak pierwsze linie zostaną wypisane, wypis wsteczny zostanie wyświetlony. Ponieważ żadna interpretacja HTML-a nie miejsca, wypis wsteczny będzie czytelny.
Typowe problemy i ich rozwiązania¶
Większość serwerów HTTP przechowuje w przestrzeni wymiany wyjście ze skryptów CGI dopóki skrypt się nie zakończy. To oznacza, że nie jest możliwe wyświetlenie raportu postępu na wyświetlaczu klienta kiedy skrypt działa.
Sprawdź instrukcje instalacyjne powyżej.
Sprawdź pliki logu serwera HTTP. (
tail -f logfile
w oddzielnym oknie może być użyteczne!)Zawsze sprawdzaj skrypt dla błędów składniowych najpierw, przez wykonanie czegoś podobnego do
python script.py
.Jeśli twój skrypt nie ma żadnych błędów składniowych, spróbuj dodać
import cgitb; cgitb.enable()
na górze skryptu.Gdy wywoływane są zewnętrzne programy upewnij się że mogą być znalezione. Zwykle to oznacza, że używanie nazw ścieżek absolutnych —
PATH
nie jest zwykle ustawiana na użyteczną wartość skryptu CGI.Gdy wczytujesz lub zapisujesz zewnętrzne pliki, upewnij się że mogą one być wczytane lub zapisane przez userid pod którym twój skrypt CGI będzie działał: to jest typowo userid pod którym serwer sieci działa, lub pewny jawnie określony userid dla właściwości sieciowego serwera
suexec
.Nie próbuj nadawać skryptowi CGI trybu set-uid. To nie działa na większości systemów, i jest odpowiedzialnością za bezpieczeństwo także.
Przypisy