Notes

プログラミング

  • 今回は、サーバ上で実行するコードを記述することで、より高度なWebアプリケーションを作成します。
  • 前回、CS50 IDEのhttp-serverWebサーバとして使用しました。これは、接続と要求をリッスンし、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.pyには、このアプリケーションに必要なライブラリのリストが含まれています。
    • staticは、CSSやJavaScriptファイルなどの静的ファイルのディレクトリです。
    • templates/は、最終的なHTMLの作成に使用されるファイルのディレクトリです。
  • 一般的な言語ごとに多くのWebサーバフレームワークがありますが、Flaskは今日私たちが使用する代表的なものと言えるでしょう。
  • Flaskはまた、特定のデザインパターン、つまりプログラムやコードの構成方法を実装します。Flaskの場合、設計パターンは一般的にMVC (Model-view-controller) と呼ばれるものです。
  • コントローラ (controller) は、ユーザ入力を受けてアプリケーション全体を管理するロジックとコードです。Flaskでは、これがPythonコードになります。
    • ビュー (view) は、ユーザが表示して操作するHTMLやCSSなどのユーザ・インターフェースです。
    • モデル (model) は、SQLデータベースやCSVファイルなど、アプリケーションのデータです。
  • 最も単純なFlaskアプリケーションは次のようになります。
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "hello, world"
  • まず、flaskライブラリからFlaskをインポートします。Flaskライブラリでは、メイン名に大文字が使用されています。
    • 次に、Flask変数にファイルの名前を指定して、app変数を作成します。
    • 次に、ルート/、つまりURLに@app.routeという関数のラベルを付けます。Pythonの@記号はデコレータと呼ばれ、ある関数を別の関数に適用します。
    • デフォルトページである/の要求に応答するため、関数indexを呼び出します。ここでは、関数は文字列で応答します。
  • CS50 IDEでは、アプリケーションコードを含むディレクトリに移動し、flask runと入力して起動します。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, worldhelloに置き換えて、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で同じ入力が返されます。
  • Googleの検索クエリ/search?q=catsは、パラメータqのコードによって解析され、関連するすべての結果を取得するためにデータベースに渡されます。これらの結果は、最終的な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>
  • フォームをルート/greetに送信し、nameパラメータとsubmitボタンに入力します。
    • 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のフォームは、毎回同じにすることができるので静的です。
    • これで、サーバを実行し、デフォルトページのフォームを表示し、それを使用して別のページを生成できます。

POST

  • 上記のフォームでは、フォームのデータをURLに含むGETメソッドを使用しました。
  • HTMLのメソッドを<form action="/greet" method="post">として変更します。また、POSTメソッドを受け入れるようにコントローラを変更し、パラメータを別の場所で探す必要があります。
@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を使用する必要があります。
  • これらの変更を行った後でアプリケーションを再起動すると、フォームは/greetに移動しますが、コンテンツはURLに含まれなくなります。

レイアウト

  • index.htmlgreet.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")
  • フォームの値が有効であることを確認し、実際にはまだデータを処理していない場合でも、結果に応じてテンプレートを返します。
  • ただし、ユーザーはブラウザでフォームのHTMLを変更し、選択したオプションとして他のスポーツを含むリクエストを送信できます。
  • application.pyにリストを作成して、sportの値が有効であることを確認します。
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リクエストで送信されたsportapplication.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 %}
  • register関数で、request.form.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コマンドを使用して、事前に作成されたidname、および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
...
  • クッキーは、ブラウザが保存してくれるウェブサーバからの小さなデータです。多くの場合、これらは大きな乱数または文字列であり、訪問ごとにユーザを一意に識別し追跡するために使用されます。
    • この場合、サーバはブラウザに、そのサーバのCookie (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に名前が設定されていない場合は/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()では、デフォルトルートはデータベースに格納されているブックのリストをレンダリングします。
    • templates/books.htmlには、本booksのリストと、それぞれの本の 「カートに追加 (Add to Cart)」 をクリックできるフォームが表示されます。
    • /cartルートは、POSTリクエストのidをリストのセッション変数に保存します。しかし、リクエストがGETメソッドを使用した場合、/cartは、セッションに格納されているidのリストに一致するidを持つブックのリストを表示します。
  • そのため、Webサイト上の 「ショッピングカート (shopping carts)」 は、サーバに格納されたクッキーとセッション変数を使用して実装できます。
  • デフォルトルートで生成されたソースを表示すると、各ブックに独自の<form>要素があることがわかります。それぞれが異なるid入力を持ち、非表示で生成されます。このidはサーバ上のSQLiteデータベースから取得され、/cartルートに送り返されます。
  • 別の例として、フロントエンド (ユーザ側) でJavaScriptを使用し、バックエンド (サーバ側)でPythonを使用する方法を示します。
  • application.pyにあるデータベースshows.dbを開きます。
from cs50 import SQL
from flask import Flask, render_template, request

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を使用します。$.getは、JQueryライブラリ関数を呼び出して、入力の値でGETリクエストを作成します。その後、変数showsが示すように、レスポンスは匿名関数に渡され、DOMが回答のデータに基づいて生成された<li>要素とともに設定されます。
  • $.getAJAX呼び出しで、ページがロードされた後にJavaScriptが追加のHTTPリクエストを行って、より多くのデータを取得できるようにします。「Network」 タブをもう一度開くと、実際に、押した各キーが別のリクエストを行い、そのレスポンスが次のように表示されることがわかります。
  • ネットワーク要求は遅いかもしれないので、$.getに渡す匿名関数はコールバック関数であり、サーバから応答を受け取った後にのみ呼び出されます。その間、ブラウザは他のJavaScriptコードを実行することができます。
  • 今回はここまでです!