Webプログラミング
今回は、サーバ上で実行するコードを記述することで、より高度なWebアプリケーションを作成します。 前回、CS50 IDEのhttp-server
をWebサーバ として使用しました。これは、接続と要求をリッスンし、Webページやその他のリソースで応答するプログラムです。 HTTPリクエストには、次のようなヘッダーがあります。
GET / HTTP/1.1
...
これらのヘッダは、ファイルやページを要求したり、ブラウザからサーバにデータを送り返したりできます。 http-server
は静的ページでのみ応答しますが、GET /search?q=cats HTTP/1.1
のように、リクエストヘッダーを解析または分析する他のWebサーバーを使用し、ページを動的に返すことができます。
Flask
PythonとFlask というライブラリを使用して、独自のWebサーバを作成し、追加機能を実装します。Flaskはフレームワーク でもあり、コードのライブラリには、それをどのように使うべきかについての一連の規約も含まれています。たとえば、他のライブラリと同様に、Flaskにはリクエストを個別に解析するために使用できる関数が含まれていますが、フレームワークとして、プログラムのコードを特定の方法(フレームワークのルールに則った方法)で組む必要があります。
application.py
requirements.txt
static/
templates/
application.py
には、Webサーバ用のPythonコードがあります。requirements.
txtには、このアプリケーションに必要なライブラリのリストが含まれています。static
は、CSSやJavaScriptファイルなどの静的ファイルのディレクトリです。templates/
は、最終的なHTMLの作成に使用されるファイルのディレクトリです。一般的な言語ごとに多くのWebサーバフレームワークがありますが、Flaskは今日私たちが使用する代表的なものと言えるでしょう。
Flaskはまた、特定のデザインパターン 、つまりプログラムやコードの構成方法を実装します。Flaskの場合、デザインパターンは一般的にMVC (Model-view-controller) と呼ばれるものです。
コントローラ (controller) は、ユーザ入力を受けてアプリケーション全体を管理するロジックとコードです。Flaskでは、これがPythonコードになります。ビュー (view) は、ユーザが表示して操作するHTMLやCSSなどのユーザ・インターフェースです。 モデル (model) は、SQLデータベースやCSVファイルなど、アプリケーションのデータです。 最も単純なFlaskアプリケーションは次のようになります。application.py
というファイル名で保存しましょう。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "hello, world"
まず、flask
ライブラリからFlask
をインポートします。Flaskライブラリでは、先頭大文字のメイン名が使用されています。次に、Flask
変数にファイルの名前を指定して、app
変数を作成します。 次に、ルート/
、URLとURL用の関数を@app.route
を使ってラベル付けします。Pythonの@
マークはデコレーターと呼ばれ、ある関数を別の関数に紐付けするためのものです。 デフォルトページである/
の要求に応答するため、@app.route
で紐付けられた関数index
を呼び出します。index関数は文字列を応答するだけです。 CS50 IDEでサーバーを起動するには、アプリケーションコードapplication.py
を含むディレクトリに移動し、flask run
と入力して起動します。URLが表示されるので、そのURLを開いてhello, world
が表示されていることを確認します。
render_template
関数を使用してHTMLを実際に返すようにコードを更新します。この関数は、指定されたファイルを検索し、その内容を返します。
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
templates/
ディレクトリを作成し、その中にコンテンツを含むindex.html
ファイルを作成する必要があります。flask run
と入力すると、サーバのURLにアクセスしたときにHTMLファイルが返されます。コントローラのコードに書いているrender_template
に引数を渡してみましょう。
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html", name=request.args.get("name", "world"))
render_template
には、name
のような任意の名前付き引数を与えることができ、テンプレートやHTMLファイルではプレースホルダで置き換えられます。index.html
では、hello, world
をhello
に置き換えて、Flaskに変数name
の置き換え先を指示します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>hello</title>
</head>
<body>
hello, {{ name }}
</body>
</html>
Flaskライブラリのrequest
変数を使用して、HTTPリクエストからパラメータ (この場合はname
も) を取得し、パラメータが指定されていない場合はデフォルトのworld
に戻すことができます。これらの変更を行った後でサーバを再起動し、デフォルトページのURLに/?name=David
などを加えてアクセスすることで、サーバで生成されたHTMLに引数(name=David
の部分)と同じ文字列が埋め込まれて表示されます。 Googleの検索クエリである/search?q=cats
も、何らかのコードによってq
パラメータが解析され、関連するすべての結果を得るためにデータベースに渡されていると推測できます。そして、それらの結果をもとに、最終的なHTMLページが生成されます。
元のテンプレートをindex.html
からgreet.html
にファイル名を変更して、ユーザーの名前を表示します。新たに作るindex.html
に下記のようなフォームを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>hello</title>
</head>
<body>
<form action="/greet" method="get">
<input name="name" type="text">
<input type="submit">
</form>
</body>
</html>
@app.route("/greet")
で設定したルート/greet
にフォームを送信し、name
パラメータとsubmitボタンの入力項目が配置された上記のindex.html
を作成します。applications.py
コントローラでは、ルート/greet
用の関数も追加する必要があります。これは、以前/
に対して行ったものとほぼ同じです。
@app.route("/")
def index():
return render_template("index.html")
@app.route("/greet")
def greet():
return render_template("greet.html", name=request.args.get("name", "world"))
index.html
のフォームは、毎回同じにすることができるので静的です。これで、flask run
を実行してサーバを起動し、デフォルトページ「/
」のフォームを表示し、index.html
のフォームを使ってgreet.htmlを表示することが出来ます。
POST
これまで作成してきた上記のフォームでは、データをURLに含むGETメソッドを使用していました。 HTMLのメソッドを<form action="/greet" method="post">
として変更します。また、POSTメソッドを受け入れるようにapplications.py
コントローラを変更し、パラメータもrequest.args
からではなく別の場所を探す必要があります。
@app.route("/greet", methods=["POST"])
def greet():
return render_template("greet.html", name=request.form.get("name", "world"))
request.args
はGETリクエストのパラメータ用ですが、POSTリクエストのパラメータにはFlaskのrequest.form
を使用する必要があります。これらの変更を行った後でアプリケーションを再起動すると、フォームのsubmitボタンを押すことで/greet
ページに移動しますが、ページ内容がURLのパラメータ(?name=David
のようなもの)に含まれなくなります。
レイアウト
index.html
とgreet.html
には、繰り返されるHTMLコードがあります。HTMLだけではファイル間でコードを共有することはできませんが、Flaskテンプレート (およびその他のWebフレームワーク) を使用すれば、このような共通のコンテンツを排除できます。別のテンプレートlayout.html
を作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>hello</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
Flaskは、{% %}
構文を使用してプレースホルダブロックや他のコードの塊・部品をインクルードするテンプレート言語であるJinjaをサポートします。ここでは、<body>
要素に含めるHTMLが含まれているブロックにbody
と名前を付けました。
index.html
では、layout.html
を配置図として使用し、body
ブロックを次のように定義します。
{% extends "layout.html" %}
{% block body %}
<form action="/greet" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<input type="submit">
</form>
{% endblock %}
同様に、greet.html
では、挨拶文だけのbody
ブロックを定義しています。
{% extends "layout.html" %}
{% block body %}
hello, {{ name }}
{% endblock %}
ここで、サーバを再起動し、サーバのURLを開いた後にHTMLのソースを表示する(ブラウザの表示部分で右クリックし、ページのソースを表示)と、Flaskによって生成されたHTMLファイル内にフォームを含む完全なページが表示されます。 GETメソッドとPOSTメソッドの両方をサポートするために、同じルートを再利用することもできます。
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
return render_template("greet.html", name=request.form.get("name", "world"))
return render_template("index.html")
最初に、リクエストrequest
のメソッドmethod
がPOSTリクエストかどうかを確認します。POSTリクエストの場合は、name
パラメータを検索し、greet.html
テンプレートからHTMLを返します。それ以外の場合は、フォームを持つindex.html
からHTMLを返します。フォームのアクションaction
もデフォルトのルート/
に変更する必要があります。
Frosh IMs
Davidの最初のWebアプリケーションのひとつは、キャンパスの学生が校内スポーツである「frosh IM」に登録するためのものでした。 以前と同様のlayout.html
を使用します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="initial-scale=1, width=device-width">
<title>froshims</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
<head>
の中に<meta>
タグを使用すると、ページにさらにメタデータを追加できます。この場合、ページのサイズとフォントをデバイスに自動的にスケーリングするようにブラウザに指示するために、viewport
メタデータのcontent
属性を追加します。application.py
では、デフォルトのルート/
用のindex.html
テンプレートを返します。
from flask import Flask, render_template, request
app = Flask(__name__)
SPORTS = [
"Dodgeball",
"Flag Football",
"Soccer",
"Volleyball",
"Ultimate Frisbee"
]
@app.route("/")
def index():
return render_template("index.html")
index.html
テンプレートは次のようになります。
{% extends "layout.html" %}
{% block body %}
<h1>Register</h1>
<form action="/register" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<select name="sport">
<option disabled selected value="">Sport</option>
<option value="Dodgeball">Dodgeball</option>
<option value="Flag Football">Flag Football</option>
<option value="Soccer">Soccer</option>
<option value="Volleyball">Volleyball</option>
<option value="Ultimate Frisbee">Ultimate Frisbee</option>
</select>
<input type="submit" value="Register">
</form>
{% endblock %}
以前のようなフォームを用意し、各スポーツのオプションを含む<select>
メニューを用意します。 application.py
では、/register
ルートに対してPOSTを許可します。
@app.route("/register", methods=["POST"])
def register():
if not request.form.get("name") or not request.form.get("sport"):
return render_template("failure.html")
return render_template("success.html")
from flask import Flask, render_template, request
app = Flask(__name__)
SPORTS = [
"Dodgeball",
"Flag Football",
"Soccer",
"Volleyball",
"Ultimate Frisbee"
]
@app.route("/")
def index():
return render_template("index.html", sports=SPORTS)
...
そのリストをindex.html
テンプレートに渡します。 テンプレートでは、ループを使用して、sports
として渡される文字列のリストからオプションのリストを生成することもできます。
...
<select name="sport">
<option disabled selected value="">Sport</option>
{% for sport in sports %}
<option value="{{ sport }}">{{ sport }}</option>
{% endfor %}
</select>
...
最後に、POSTリクエストで送信されたsport
がapplication.py
のSPORTSリストにあるかどうかを確認します。
...
@app.route("/register", methods=["POST"])
def register():
if not request.form.get("name") or request.form.get("sport") not in SPORTS:
return render_template("failure.html")
return render_template("success.html")
フォームの選択メニューをチェックボックスに変更して、複数のスポーツの選択を許可できます。
{% extends "layout.html" %}
{% block body %}
<h1>Register</h1>
<form action="/register" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
{% for sport in sports %}
<input name="sport" type="checkbox" value="{{ sport }}"> {{ sport }}
{% endfor %}
<input type="submit" value="Register">
</form>
{% endblock %}
チェックボックスの値をapplication.py
で使う場合、register
関数でrequest.form.getlist
を呼び出して、チェックされたオプションのリストを取得します。(チェックボックスでは値が複数あるので、get()
からgetlist()
に書き直す必要があることに注意しましょう。) ラジオボタンを使用して、一度に1つのオプションのみを選択することもできます。
データの保存
登録された学生または登録者を、Webサーバのメモリ内の辞書に保存します。
from flask import Flask, redirect, render_template, request
app = Flask(__name__)
REGISTRANTS = {}
...
@app.route("/register", methods=["POST"])
def register():
name = request.form.get("name")
if not name:
return render_template("error.html", message="Missing name")
sport = request.form.get("sport")
if not sport:
return render_template("error.html", message="Missing sport")
if sport not in SPORTS:
return render_template("error.html", message="Invalid sport")
REGISTRANTS[name] = sport
return redirect("/registrants")
REGISTRANTS
という辞書を作成し、register
でまず名前name
とスポーツsport
をチェックし、それぞれの場合に異なるエラーメッセージを返します。そうすれば、名前とスポーツをREGISTRANTS
辞書に安全に保存し、登録された学生を表示する別のルートにリダイレクトできます。一方、エラーメッセージテンプレートは単に次のメッセージを表示します。
{% extends "layout.html" %}
{% block body %}
{{ message }}
{% endblock %}
登録した学生を表示するために、/registrants
ルートとテンプレートを追加してみましょう。
@app.route("/registrants")
def registrants():
return render_template("registrants.html", registrants=REGISTRANTS)
ルートでは、REGISTRANTS
ディクショナリをregistrants
というパラメータとしてテンプレートに渡します。
{% extends "layout.html" %}
{% block body %}
<h1>Registrants</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Sport</th>
</tr>
</thead>
<tbody>
{% for name in registrants %}
<tr>
<td>{{ name }}</td>
<td>{{ registrants[name] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
テンプレートにはテーブルがあり、各キーと値の見出し行と行がregistrants
に保存されます。 Webサーバが停止すると、保存されているデータが失われるため、cs50
のSQLライブラリでSQLiteデータベースを使用します。
from cs50 import SQL
from flask import Flask, redirect, render_template, request
app = Flask(__name__)
db = SQL("sqlite:///froshims.db")
...
IDEターミナルで、sqlite3 froshims.db
を実行してデータベースを開き、.schema
コマンドを使用して、事前に作成されたid
、name
、およびsport
の列を持つ表を表示できます。 このルートでは、SQLを使用して行を挿入および選択できます。
@app.route("/register", methods=["POST"])
def register():
name = request.form.get("name")
if not name:
return render_template("error.html", message="Missing name")
sport = request.form.get("sport")
if not sport:
return render_template("error.html", message="Missing sport")
if sport not in SPORTS:
return render_template("error.html", message="Invalid sport")
db.execute("INSERT INTO registrants (name, sport) VALUES(?, ?)", name, sport)
return redirect("/registrants")
@app.route("/registrants")
def registrants():
registrants = db.execute("SELECT * FROM registrants")
return render_template("registrants.html", registrants=registrants)
要求を検証したら、INSERT INTO
を使用して行を追加できます。同様に、registrants()
では、すべての行をSELECT
し、行のリストとしてテンプレートに渡すことができます。 db.execute
から返される各行は辞書であるため、registrants.html
テンプレートも調整する必要があります。したがって、registrant.name
およびregistrant.sport
を使用して、各行の各キーの値にアクセスできます。
<tbody>
{% for registrant in registrants %}
<tr>
<td>{{ registrant.name }}</td>
<td>{{ registrant.sport }}</td>
<td>
<form action="/deregister" method="post">
<input name="id" type="hidden" value="{{ registrant.id }}">
<input type="submit" value="Deregister">
</form>
</td>
</tr>
{% endfor %}
</tbody>
別のライブラリflask_mail
を使用してユーザに電子メールを送信することもできます。
import os
import re
from flask import Flask, render_template, request
from flask_mail import Mail, Message
app = Flask(__name__)
app.config["MAIL_DEFAULT_SENDER"] = os.getenv("MAIL_DEFAULT_SENDER")
app.config["MAIL_PASSWORD"] = os.getenv("MAIL_PASSWORD")
app.config["MAIL_PORT"] = 587
app.config["MAIL_SERVER"] = "smtp.gmail.com"
app.config["MAIL_USE_TLS"] = True
app.config["MAIL_USERNAME"] = os.getenv("MAIL_USERNAME")
mail = Mail(app)
機密性の高い変数をコードの外部、つまりIDEの環境に設定し、コードに含めないようにします。メールを送信する変数Mail
に、ユーザ名、パスワード、メールサーバ (この場合はGmail) などの設定詳細を指定できます。 最後に、ルートregister
では、ユーザに電子メールを送信できます。
@app.route("/register", methods=["POST"])
def register():
email = request.form.get("email")
if not email:
return render_template("error.html", message="Missing email")
sport = request.form.get("sport")
if not sport:
return render_template("error.html", message="Missing sport")
if sport not in SPORTS:
return render_template("error.html", message="Invalid sport")
message = Message("You are registered!", recipients=[email])
mail.send(message)
return render_template("success.html")
フォームでは、名前の代わりに電子メールも要求する必要があります。
<input autocomplete="off" name="email" placeholder="Email" type="email">
サーバを再起動し、フォームを使用してメールを送信すると、実際にメールが送信されます。
セッション
セッション とは、Webサーバが各ユーザに関する情報をサーバーに記憶する方法のことで、ユーザがログインしたままでいられるようにするなどの機能を可能にします。その結果、サーバーはレスポンスの中にSet-Cookie
と呼ばれる別のヘッダーを送信できることがわかりました。
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: session=value
...
クッキー は、ブラウザに保存するウェブサーバからの小さなデータです。多くの場合、これらは大きな乱数または文字列であり、訪問ごとにユーザを一意に識別し追跡するために使用されます。この場合、サーバーは私たちのブラウザに、そのサーバー用のクッキーをsession
と言う名前でvalue
という値を設定するように求めています。 その後、ブラウザが同じサーバに対して別のリクエストを行う時、同じサーバが以前に設定したCookieをブラウザからサーバーに送信します。
GET / HTTP/1.1
Host: gmail.com
Cookie: session=value
現実の世界では、遊園地に行けばハンドスタンプを押してもらうことで、再入園することができます。同様に、私たちのブラウザは私たちのクッキをウェブサーバに返しているので、私たちが誰であるかを記憶することができます。 広告会社は、多数のWebサイトからクッキーを設定して、すべてのWebサイトのユーザを追跡するでしょう。これとは対照的に、シークレット (Incognito) モードでは、ブラウザは以前に設定されたクッキーを送信しません。 Flaskでは、flask_session
ライブラリを使用してこれを管理できます。
from flask import Flask, redirect, render_template, request, session
from flask_session import Session
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
@app.route("/")
def index():
if not session.get("name"):
return redirect("/login")
return render_template("index.html")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
session["name"] = request.form.get("name")
return redirect("/")
return render_template("login.html")
@app.route("/logout")
def logout():
session["name"] = None
return redirect("/")
IDEのファイルシステムを使用するようにセッションライブラリを構成し、ユーザー名を格納するために辞書のようにセッションsession
を使用します。FlaskはHTTPクッキーを使用して、私たちのWebサーバにアクセスする各ユーザのセッション変数
を維持することが分かりました。各訪問者は、コード内ではグローバルであるように見えても、各自のセッション変数
を取得します。デフォルトの /
ルートでは、session
でユーザーのname
が設定されていない場合は /login
にリダイレクトし、それ以外の場合はデフォルトの index.html テンプレートを表示します。 /login
ルートについては、POST経由で送信されるフォームの値をsession
内のname
に設定し、デフォルトルート「/
」にリダイレクトします。GET経由でルートにアクセスした場合は、login.html
でログインフォームを表示します。/logout
ルートの場合、session
内のname
の値をNone
に設定してクリアし、/
に再度リダイレクトします。また、一般的には使用したいライブラリの名前を記載したrequirements.txt
が必要になりますが、ここで使用しているものはIDEにあらかじめインストールされています。 login.html
には、名前のフォームだけがあります。
{% extends "layout.html" %}
{% block body %}
<form action="/login" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<input type="submit" value="Log In">
</form>
{% endblock %}
index.html
では、session.name
が存在するかどうかをチェックし、異なるコンテンツを表示できます。
{% extends "layout.html" %}
{% block body %}
{% if session.name %}
You are logged in as {{ session.name }}. <a href="/logout">Log out</a>.
{% else %}
You are not logged in. <a href="/login">Log in</a>.
{% endif %}
{% endblock %}
サーバーを再起動し、そのURLに移動してログインすると、 「Network」 タブにブラウザが実際にヘッダーCookie:
をリクエスト時に送信していることを見ることができます。
格納、表示
例としてstore
を見てみましょう。application.py
は、データベースとセッションを使用するようにアプリケーションを初期化および構成します。index()
では、デフォルトルートはデータベースに格納されている本(books)のリストをブラウザに描画します。templates/books.html
には、本books
のリストと、それぞれの本に対して 「Add to Cart (カートに追加)」 をクリックできるフォームが表示されます。/cart
ルートは、POSTリクエストのid
をリストのセッション変数
に保存します。しかしながら、リクエストがGETメソッドであった場合、/cartルートはセッション
に保存されているid
のリストに一致するid
を持つ本booksのリストを表示します。(セッション
に「カートに追加 (Add to Cart)」ボタンが押された本のリストがあります。) そのため、Webサイト上の 「ショッピングカート (shopping carts)」 は、ブラウザに保存されたクッキーとサーバに格納されたセッション変数を使用して実装できます。 デフォルトルート「/
」で生成されたソースを表示すると、それぞれの本に独自の<form>
要素があることがわかります。それぞれが異なるid
を持つinputタグで、type="hidden"
属性により非表示で生成されます。このid
はサーバ上のSQLiteデータベースから取得され、/cart
ルートに送り返されます。 別の例として、フロントエンド (ユーザ側) でJavaScriptを使用し、バックエンド (サーバ側)でPythonを使用する方法を示します。 application.py
にあるデータベースshows.db
を開きます。shows.db
は、 https://cdn.cs50.net/2020/fall/lectures/9/src9/shows0/ にあります。
from cs50 import SQL
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
db = SQL("sqlite:///shows.db")
@app.route("/")
def index():
return render_template("index.html")
@app.route("/search")
def search():
shows = db.execute("SELECT * FROM shows WHERE title LIKE ?", "%" + request.args.get("q") + "%")
return render_template("search.html", shows=shows)
デフォルトのルート/
はフォームを表示します。ここに検索語を入力します。フォームはGETメソッドを使用して/search
に検索クエリを送信し、/search
はデータベースを使用して一致する番組のリストを検索します。最後に、search.html
テンプレートが番組のリストを表示します。 JavaScriptを使用すると、入力時に結果の一部のリストを表示できます。まず、jsonify
という関数を使用して、JavaScriptで使用できる標準形式であるJSON形式で番組を返します。
@app.route("/search")
def search():
shows = db.execute("SELECT * FROM shows WHERE title LIKE ?", "%" + request.args.get("q") + "%")
return jsonify(shows)
検索クエリを送信すると、辞書のリストが返されます。
次に、index.html
テンプレートでこのリストをDOMの要素に変換します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="initial-scale=1, width=device-width">
<title>shows</title>
</head>
<body>
<input autocomplete="off" autofocus placeholder="Query" type="search">
<ul></ul>
<script crossorigin="anonymous" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
let input = document.querySelector('input');
input.addEventListener('keyup', function() {
$.get('/search?q=' + input.value, function(shows) {
let html = '';
for (let id in shows)
{
let title = shows[id].title;
html += '<li>' + title + '</li>';
}
document.querySelector('ul').innerHTML = html;
});
});
</script>
</body>
</html>
リクエストをより簡単に行うために、別のライブラリであるJQueryを使用します。input
要素の変更を監視し、$.get
を使います。この関数はJQueryライブラリの関数を呼び出し、入力された値でGETリクエストを行います。そして、レスポンスは変数shows
として無名関数に渡され、レスポンスのデータに基づいて<li>
要素でリストを生成してDOMに設定します。
$.get
はAJAXの 呼び出しで、ページがロードされた後にJavaScriptを使って追加のHTTPリクエストを行い、より多くのデータを取得できるようにします。「Network」 タブをもう一度開くと、実際に、押した各キーが別のリクエストを行い、そのレスポンスが次のように表示されることがわかります。(ここで使われている$.get
は単純なGETリクエストを送るjQueryの関数です。)
ネットワーク要求は遅いかもしれないので、$.get
に渡す匿名関数はコールバック 関数であり、サーバから応答を受け取った後にのみ呼び出されます。その間、ブラウザは他のJavaScriptコードを実行することができます。 今回はここまでです!