From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../django/development_environment/index.html | 433 +++++++++++ files/ja/learn/server-side/django/index.html | 69 ++ .../server-side/django/introduction/index.html | 281 ++++++++ .../ja/learn/server-side/django/models/index.html | 461 ++++++++++++ .../server-side/django/skeleton_website/index.html | 398 ++++++++++ .../tutorial_local_library_website/index.html | 99 +++ .../django/web_application_security/index.html | 180 +++++ .../express_nodejs/deployment/index.html | 525 ++++++++++++++ .../development_environment/index.html | 410 +++++++++++ .../displaying_data/author_detail_page/index.html | 89 +++ .../displaying_data/author_list_page/index.html | 85 +++ .../displaying_data/book_detail_page/index.html | 112 +++ .../displaying_data/book_list_page/index.html | 68 ++ .../index.html | 91 +++ .../bookinstance_list_page/index.html | 69 ++ .../date_formatting_using_moment/index.html | 60 ++ .../flow_control_using_async/index.html | 137 ++++ .../displaying_data/genre_detail_page/index.html | 121 ++++ .../displaying_data/home_page/index.html | 133 ++++ .../express_nodejs/displaying_data/index.html | 92 +++ .../locallibrary_base_template/index.html | 69 ++ .../displaying_data/template_primer/index.html | 149 ++++ .../server-side/express_nodejs/forms/index.html | 263 +++++++ .../ja/learn/server-side/express_nodejs/index.html | 79 ++ .../installing_on_pws_cloud_foundry/index.html | 242 +++++++ .../express_nodejs/introduction/index.html | 528 ++++++++++++++ .../server-side/express_nodejs/mongoose/index.html | 799 +++++++++++++++++++++ .../server-side/express_nodejs/routes/index.html | 640 +++++++++++++++++ .../express_nodejs/skeleton_website/index.html | 512 +++++++++++++ .../tutorial_local_library_website/index.html | 98 +++ .../first_steps/client-server_overview/index.html | 329 +++++++++ files/ja/learn/server-side/first_steps/index.html | 47 ++ .../first_steps/introduction/index.html | 227 ++++++ .../first_steps/web_frameworks/index.html | 328 +++++++++ .../first_steps/website_security/index.html | 181 +++++ files/ja/learn/server-side/index.html | 59 ++ .../node_server_without_framework/index.html | 163 +++++ 37 files changed, 8626 insertions(+) create mode 100644 files/ja/learn/server-side/django/development_environment/index.html create mode 100644 files/ja/learn/server-side/django/index.html create mode 100644 files/ja/learn/server-side/django/introduction/index.html create mode 100644 files/ja/learn/server-side/django/models/index.html create mode 100644 files/ja/learn/server-side/django/skeleton_website/index.html create mode 100644 files/ja/learn/server-side/django/tutorial_local_library_website/index.html create mode 100644 files/ja/learn/server-side/django/web_application_security/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/deployment/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/development_environment/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/home_page/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/displaying_data/template_primer/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/forms/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/installing_on_pws_cloud_foundry/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/introduction/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/mongoose/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/routes/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/skeleton_website/index.html create mode 100644 files/ja/learn/server-side/express_nodejs/tutorial_local_library_website/index.html create mode 100644 files/ja/learn/server-side/first_steps/client-server_overview/index.html create mode 100644 files/ja/learn/server-side/first_steps/index.html create mode 100644 files/ja/learn/server-side/first_steps/introduction/index.html create mode 100644 files/ja/learn/server-side/first_steps/web_frameworks/index.html create mode 100644 files/ja/learn/server-side/first_steps/website_security/index.html create mode 100644 files/ja/learn/server-side/index.html create mode 100644 files/ja/learn/server-side/node_server_without_framework/index.html (limited to 'files/ja/learn/server-side') diff --git a/files/ja/learn/server-side/django/development_environment/index.html b/files/ja/learn/server-side/django/development_environment/index.html new file mode 100644 index 0000000000..ac824323ea --- /dev/null +++ b/files/ja/learn/server-side/django/development_environment/index.html @@ -0,0 +1,433 @@ +--- +title: Django 開発環境の設定 +slug: Learn/Server-side/Django/development_environment +tags: + - Python + - Webフレームワーク + - django + - サーバーサイドプログラミング +translation_of: Learn/Server-side/Django/development_environment +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}
+ +

Djangoがどういうものか知ったので、Windows、Linux(Ubuntu)、およびmacOSでDjango開発環境をセットアップしてテストする方法を説明します。一般的なオペレーティングシステムを使っていれば、Djangoアプリケーションの開発を始められます。

+ + + + + + + + + + + + +
前提条件:ターミナル/コマンドラインを開く方法を知っていること。開発用コンピュータのオペレーティングシステムにソフトウェアパッケージをインストールする方法を知っていること。
目的:あなたのコンピュータでDjango(2.0)の開発環境を動かします。
+ +

Django開発環境の概要

+ +

Djangoは自分のコンピュータをセットアップするのはとても簡単で、Webアプリケーションの開発を開始できます。このセクションでは、開発環境の内容を説明し、セットアップおよび構成オプションの一部の概要を示します。また、Ubuntu、macOS X、WindowsにDjango開発環境をインストールする際の推奨される方法と、そのテスト方法について説明します。

+ +

Django開発環境とは何ですか?

+ +

開発環境はDjangoをローカルコンピュータにインストールしたものです。Djangoアプリを開発し、運用環境にデプロイする前にテストできます。

+ +

Django自体が提供する主なツールは、Djangoプロジェクトを作成して作業するためのPythonスクリプトと、ローカルの(つまり、外部のWebサーバーではなく)Django Webアプリケーションをあなたのコンピュータ上のウェブブラウザでテストするための簡単な開発用Webサーバーです。

+ +

開発環境の一部を構成する他の周辺ツールがありますが、ここではカバーしません。これには、コードを編集するためのテキストエディタやIDE、コードの異なるバージョンを安全に管理するためのGitのようなソース管理ツールなどがあります。すでにテキストエディタがインストールされていると仮定しています。

+ +

Djangoの構成オプションとは?

+ +

Djangoは、インストール場所と設定の方法に関して非常に柔軟性があります。Djangoは次のようなことが可能です:

+ + + +

これらのオプションは、それぞれわずかに異なる構成とセットアップを必要とします。以下のサブセクションでは、いくつかの選択肢について説明します。この記事の残りの部分では、いくつかオペレーティングシステムにDjangoをセットアップする方法を説明します。このモジュールの残りの部分は、セットアップが済んでいる想定です。

+ +
+

ノート: その他のインストールオプションについては、Djangoの公式ドキュメントでカバーされています。下記で適切なドキュメントにリンクしています。

+
+ +

どのオペレーティングシステムがサポートされていますか?

+ +

DjangoのWebアプリケーションは、Python 3プログラミング言語を実行できるほとんどすべてのマシン(Windows、MacOS X、Linux / Unix、Solarisなど)で実行できます。ほとんどのコンピュータで開発中にDjangoを実行できるパフォーマンスが必要です。

+ +

この記事では、Windows、macOS X、およびLinux/Unixについて説明します。

+ +

どのバージョンのPythonを使うべきですか?

+ +

最新のバージョンを使用することをお勧めします - 執筆時点ではPython 3.6です。

+ +

必要であればPython 3.4以上を使用できます(Python 3.4のサポートは将来のリリースでは廃止される予定です)。

+ +
+

ノート: Python 2.7はDjango 2.0では使用できません(Django 1.11.x系はPython 2.7をサポートする最後のバージョンです)。

+
+ +

どこでDjangoをダウンロードできますか?

+ +

Djangoをダウンロードする場所は3つあります:

+ + + +

この記事ではPyPiからDjangoをインストールし、最新の安定版を入手する方法を説明します。

+ +

どのデータベースですか?

+ +

Djangoは4つの主要なデータベース(PostgreSQL、MySQL、Oracle、SQLite)をサポートしています。また、他の一般的なSQLデータベースやNOSQLデータベースにさまざまなレベルのサポートを提供するコミュニティライブラリもあります。 DjangoはObject-Relational Mapper(ORM)を使用して多くのデータベースの違いを抽象化していますが、回避する方が良い潜在的な問題がまだあります。

+ +

この記事(とこのモジュールのほとんど)では、データをファイルに保存するSQLiteデータベースを使用します。 SQLiteは軽量データベースとしての使用を目的としており、高度な並行性はサポートできません。 ただし、主に読み取り専用のアプリケーションには最適です。

+ +
+

ノート: Djangoは、標準ツール(django-admin)を使用してWebサイトプロジェクトを開始するときに、デフォルトでSQLiteを使用するように設定されています。 これは、追加の設定やセットアップが不要なため、入門には最適な選択です。

+
+ +

システム全体またはPythonの仮想環境にインストールするには?

+ +

Python3をインストールすると、すべてのPython3コードで共有される単一のグローバル環境が得られます。 環境に好きなPythonパッケージをインストールすることはできますが、一度に1つのパッケージしかインストールできません。

+ +
+

ノート: グローバル環境にインストールされたPythonアプリケーションは、お互いに衝突する可能性があります(例えば、同じパッケージの異なるバージョンに依存する場合)。 

+
+ +

Djangoをデフォルト/グローバル環境にインストールすると、コンピュータ上でDjangoの1つのバージョンのみを対象にできます。 古いバージョンに依存しているWebサイトを維持しながら、新しいWebサイト(Djangoの最新バージョンを使用)を作成したい場合、これは問題になる可能性があります。

+ +

その結果、経験豊富なPython/Django開発者は通常、独立したPython仮想環境内でPythonアプリケーションを実行します。これにより、1台のコンピュータ上で複数の異なるDjango環境を使用することができます。Djangoの開発チームは、Pythonの仮想環境を使用することをお勧めしています。

+ +

このモジュールは以下に示す方法で、仮想環境にDjangoをインストールすることを前提としています。

+ +

Python 3のインストール

+ +

Djangoを使用するには、オペレーティングシステムにPythonをインストールする必要があります。Python 3を使用している場合は、Djangoとその他のPythonアプリケーションで使用されるPythonパッケージ/ライブラリの管理(インストール、更新、削除)に使用する Python Package Indexツール『pip3』も必要です。

+ +

このセクションでは、Ubuntu Linux 16.04、macOS X、およびWindows 10のPythonのバージョンを確認し、必要に応じて新しいバージョンをインストールする方法を簡単に説明します。

+ +
+

ノート: 使用しているプラットフォームによっては、オペレーティングシステム独自のパッケージマネージャやその他のメカニズムを使ってPython/pipをインストールできます。ほとんどのプラットフォームでは、 https://www.python.org/downloads/ から必要なインストールファイルをダウンロードし、適切なプラットフォーム固有の方法を使用してインストールできます。

+
+ +

Ubuntu 16.04

+ +

Ubuntu Linux 16.04 LTSにはデフォルトでPython 3.5.2が含まれています。 これを確認するには、bash端末で次のコマンドを実行します:

+ +
python3 -V
+ Python 3.5.2
+ +

しかし、Python 3(Djangoを含む)用のパッケージをインストールするために必要なPython Package Indexツールは、デフォルトでは利用できません。 次のコマンドを使用してbash端末にpip3をインストールできます:

+ +
sudo apt-get install python3-pip
+
+ +

macOS X

+ +

macOS X "El Capitan"やその他の最近のバージョンにはPython 3は含まれていません。これはbash端末で次のコマンドを実行することで確認できます:

+ +
python3 -V
+ -bash: python3: command not found
+ +

python.orgのPython 3(pip3ツールと一緒に)を簡単にインストールできます:

+ +
    +
  1. 必要なインストーラをダウンロードします: +
      +
    1. https://www.python.org/downloads/ を開きます
    2. +
    3. Download Python 3.6.4 ボタンを選択します(正確なマイナーバージョン番号は異なる場合があります)。
    4. +
    +
  2. +
  3. Finderを使用してファイルを探し、パッケージファイルをダブルクリックします。インストールの後、プロンプトが表示されます。
  4. +
+ +

以下に示すように、Python 3を確認することで、正常にインストールされたことを確認できます:

+ +
python3 -V
+ Python 3.6.4
+
+ +

同様にpip3がインストールされていることを確認するには、利用可能なパッケージを一覧表示します:

+ +
pip3 list
+ +

Windows 10

+ +

WindowsにはデフォルトでPythonは含まれていませんが、python.orgからpip3ツールと一緒に簡単にインストールできます:

+ +
    +
  1. 必要なインストーラをダウンロードします: +
      +
    1. https://www.python.org/downloads/ を開きます
    2. +
    3. Download Python 3.6.4 ボタンを選択します(正確なマイナーバージョン番号は異なる場合があります)。
    4. +
    +
  2. +
  3. ダウンロードしたファイルをダブルクリックし、インストールのプロンプトに従ってPythonをインストールします。
  4. +
+ +

Python 3がインストールされたことを確認するには、コマンドプロンプトに次のテキストを入力します:

+ +
py -3 -V
+ Python 3.6.4
+
+ +

Windowsインストーラには、デフォルトでpip3(Pythonパッケージマネージャ)が組み込まれています。次に示すようにインストールされたパッケージを一覧表示できます:

+ +
pip3 list
+
+ +
+

ノート: インストーラは、上記のコマンドが動作するために必要なものすべてをセットアップする必要があります。Pythonが見つからないというメッセージが表示された場合は、システムパスに追加する必要があります。

+
+ +

Python仮想環境内でのDjangoの使用

+ +

仮想環境を作成するために使用するライブラリは、 virtualenvwrapper (LinuxとmacOS X)とvirtualenvwrapper-win(Windows)です。これらはどちらもvirtualenvツールを使用します。ラッパーツールは、すべてのプラットフォーム上のインターフェイスを管理するための一貫したインターフェイスを作成します。

+ +

仮想環境ソフトウェアのインストール

+ +

Ubuntu仮想環境のセットアップ

+ +

Pythonとpipをインストールした後、virtualenvwrapper(virtualenvを含む)をインストールできます。公式インストールガイドはこちら、または下記の手順に従ってください。

+ +

pip3を使用してツールをインストールします:

+ +
sudo pip3 install virtualenvwrapper
+ +

次に、シェルのスタートアップファイルの最後に次の行を追加します(これはホームディレクトリ内の .bashrc という名前の隠しファイルです)。これらは、仮想環境の存在場所、開発プロジェクトディレクトリの場所、およびこのパッケージと共にインストールされるスクリプトの場所を設定します:

+ +
export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export VIRTUALENVWRAPPER_VIRTUALENV_ARGS=' -p /usr/bin/python3 '
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh
+ +
+

ノート: VIRTUALENVWRAPPER_PYTHONおよびVIRTUALENVWRAPPER_VIRTUALENV_ARGS変数は、Python3の通常のインストール場所を指し、source /usr/local/bin/virtualenvwrapper.shvirtualenvwrapper.sh スクリプトの通常の場所を指します。テスト時にvirtualenvが動作しない場合は、Pythonとスクリプトが予想される場所にあることを確認してから、起動ファイルを適切に変更してください。
+
+ あなたのシステムでの正しい場所は、which virtualenvwrapper.shwhich python3というコマンドを使って見つけることができます。

+
+ +

次に、ターミナルで次のコマンドを実行してスタートアップファイルをリロードします:

+ +
source ~/.bashrc
+ +

この時点で、以下に示すように一連のスクリプトが実行されているはずです:

+ +
virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/premkproject
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postmkproject
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/preactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/get_env_details
+
+ +

これで、mkvirtualenvコマンドを使用して新しい仮想環境を作成できます。

+ +

macOS X仮想環境のセットアップ

+ +

macOS Xでのvirtualenvwrapperのセットアップは、Ubuntuの場合とほぼ同じです(オフィシャルインストールガイドまたはそれ以下の指示に従います)。

+ +

次に示すようにpipを使用してvirtualenvwrapper(およびvirtualenvをバンドル)をインストールします。

+ +
sudo pip3 install virtualenvwrapper
+ +

次に、シェルスタートアップファイルの最後に次の行を追加します。

+ +
export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh
+
+ +
+

ノートVIRTUALENVWRAPPER_PYTHON変数はPython3の通常のインストール場所を指し、source /usr/local/bin/virtualenvwrapper.shvirtualenvwrapper.shスクリプトの通常の場所を指しています。テスト時にvirtualenvが動作しない場合は、Pythonとスクリプトが予想される場所にあることを確認してから、起動ファイルを適切に変更してください。

+ +

 

+ +

たとえば、macOSでの1つのインストールテストでは、スタートアップファイルに次の行が必要になりました:

+ +
export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /Library/Frameworks/Python.framework/Versions/3.7/bin/virtualenvwrapper.sh
+ +

 

+ +

あなたのシステムでの正しい場所は、which virtualenvwrapper.shwhich python3というコマンドを使って見つけることができます。

+
+ +


+ これらはUbuntuの場合と同じ行ですが、スタートアップファイルはホームディレクトリ内で .bash_profile という別の名前の隠しファイルです。

+ +
+

ノート: 編集する.bash-profileがFinderで見つからない場合は、ターミナルでnanoを使用して開くこともできます。

+ +

コマンドは次のようになります:

+ +
cd ~  # Navigate to my home directory
+ls -la #List the content of the directory. YOu should see .bash_profile
+nano .bash_profile # Open the file in the nano text editor, within the terminal
+# Scroll to the end of the file, and copy in the lines above
+# Use Ctrl+X to exit nano, Choose Y to save the file.
+
+ +

 

+
+ +

ターミナルで次の呼び出しを行うことによって、スタートアップファイルをリロードします:

+ +
source ~/.bash_profile
+ +

この時点で、たくさんのスクリプトが実行されているのを見ることができます(Ubuntuのインストールと同じスクリプト)。mkvirtualenvコマンドを使用して新しい仮想環境を作成できるようになりました。

+ +

Windows 10仮想環境のセットアップ

+ +

virtualenvwrapper-winのインストールは、仮想環境情報を保存する場所(デフォルト値があります)を設定する必要がないため、virtualenvwrapperを設定するより簡単です。コマンドプロンプトで次のコマンドを実行するだけです:

+ +
pip3 install virtualenvwrapper-win
+ +

これで、mkvirtualenvコマンドで新しい仮想環境を作成できます。

+ +

仮想環境の作成

+ +

virtualenvwrapperまたはvirtualenvwrapper-winをインストールすると、仮想環境での作業はすべてのプラットフォームでほとんど同様になります。

+ +

mkvirtualenv コマンドを使用して新しい仮想環境を作成できます。このコマンドが実行されると、セットアップされる環境が表示されます(表示されるのはプラットフォーム固有のものです)。コマンドが完了すると、新しい仮想環境がアクティブになります。これは、プロンプトの開始が環境の名前(下に示すように)になるので、確認できます。

+ +
$ mkvirtualenv my_django_environment
+
+Running virtualenv with interpreter /usr/bin/python3
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/t_env7/bin/get_env_details
+(my_django_environment) ubuntu@ubuntu:~$
+
+ +

これで、Djangoをインストールして開発を開始できる仮想環境の中に入りました。

+ +
+

ノート: これ以降、この記事(と実際にはモジュール)では、上でセットアップしたようなPython仮想環境内でコマンドが実行されると仮定してください。

+
+ +

仮想環境の使用

+ +

他にも知っておくべき便利なコマンドがいくつかあります(ツールのドキュメントには多くのものがありますが、これらはいつも使用するコマンドです):

+ + + +

Djangoのインストール

+ +

仮想環境を作成し、workonでその環境に入ってから、pip3を使用してDjangoをインストールできます。

+ +
pip3 install django
+
+ +

Djangoがインストールされていることをテストするには、次のコマンドを実行します(PythonがDjangoモジュールを見つけることができます):

+ +
# Linux/macOS X
+python3 -m django --version
+ 2.0
+
+# Windows
+py -3 -m django --version
+ 2.0
+
+ +
+

ノート: Windowsではpy -3コマンドをプレフィックスにしてPython 3スクリプトを起動し、Linux/macOS Xでは python3を実行します。

+
+ +
+

重要: このモジュールの残りの部分は、 Python 3 (python3) を呼び出すためにLinuxコマンドを使用します。Windowsで作業している場合は、単にこの接頭辞をpy -3に置き換えてください。

+
+ +

インストールのテスト

+ +

上記のテストはうまくいきますが、それほど楽しいことではありません。より面白いテストは、スケルトンプロジェクトを作成し、それが動作することを確認することです。これを行うには、コマンドプロンプト/ターミナルでDjangoアプリケーションを保存する場所に移動します。テストサイト用のフォルダを作成し、そのサイトに移動します。

+ +
mkdir django_test
+cd django_test
+
+ +

次に示すように、django-adminツールを使用して、 "mytestsite"という新しいスケルトンサイトを作成します。サイトを作成したら、manage.pyというプロジェクト管理用のメインスクリプトがあるフォルダに移動します。

+ +
django-admin startproject mytestsite
+cd mytestsite
+ +

次に示すようにmanage.pyrunserverコマンドを使用して、このフォルダ内から開発用Webサーバーを実行できます。

+ +
$ python3 manage.py runserver
+Performing system checks...
+
+System check identified no issues (0 silenced).
+
+You have 14 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
+Run 'python manage.py migrate' to apply them.
+
+December 29, 2017 - 03:03:47
+Django version 2.0, using settings 'mytestsite.settings'
+Starting development server at http://127.0.0.1:8000/
+Quit the server with CONTROL-C.
+
+ +
+

ノート: 上記のコマンドは、Linux/macOS Xコマンドを示しています。この時点では、 "14 unapplied migration(s)" という警告は無視できます!

+
+ +

サーバーが稼働したら、ローカルWebブラウザで http://127.0.0.1:8000/ というURLに移動して、サイトを表示できます。次のようなサイトが表示されます:

+ +

Django Skeleton App Homepage

+ + + +

要約

+ +

Django開発環境をあなたのコンピュータ上で稼働できるようになりました。

+ +

テストのセクションでは、django-admin startprojectを使用して新しいDjango Webサイトを作成する方法と、開発用Webサーバー(python3 manage.py runserver)を使用してブラウザで実行する方法についても簡単に説明しました。次の記事では、このプロセスを拡張して、シンプルで完全なWebアプリケーションを構築します。

+ +

参考文献

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}

+ +

このモジュール内

+ + + +

 

diff --git a/files/ja/learn/server-side/django/index.html b/files/ja/learn/server-side/django/index.html new file mode 100644 index 0000000000..cdb4304f5d --- /dev/null +++ b/files/ja/learn/server-side/django/index.html @@ -0,0 +1,69 @@ +--- +title: Djangoウェブフレームワーク (Python) +slug: Learn/Server-side/Django +tags: + - Python + - django + - ウェブアプリケーションフレームワーク + - サーバーサイドプログラミング + - プログラミング + - 初心者 + - 学習 + - 概要 +translation_of: Learn/Server-side/Django +--- +
{{LearnSidebar}}
+ +

Django は、Python で書かれた高機能なサーバーサイドウェブフレームワークで、とても普及しています。このモジュールは、Django が最も人気のあるウェブサーバーフレームワークの 1 つである理由、開発環境の設定方法、独自のウェブアプリケーションを作成する方法を説明します。

+ +

前提条件

+ +

このモジュールを進める前に、Django を知る必要はありません。理想は、サーバー・サイドのウェブサイトプログラミング第一歩 を読んで、サーバーサイドのウェブプログラミングとウェブフレームワークについて理解しておくことです。

+ +

プログラミングの概念と Python に関する一般的な知識はあるとよいですが、コアの概念を理解することは必須ではありません。

+ +
+

: Python は、初心者が読んで理解しやすい最も簡単なプログラミング言語の 1 つです。つまり、あなたがこのモジュールをより理解したいのであれば、インターネット上の多数の無料の書籍やチュートリアルがあなたを助けてくれます。(新しいプログラマーは python.org wiki の Python for Non Programmers ページをチェックすると良いでしょう。)

+
+ +

ガイド

+ +
+
Django の紹介
+
最初の Django の記事では、 "Django とは何ですか?" という疑問に答え、このウェブフレームワークの特徴と概要を説明します。主な機能の概要と、このモジュールで詳しく説明しない高度な機能などを紹介します。また、Django アプリケーションの主要な構成部品のいくつかを示して、使いはじめる前にどのように使うのか、何ができるのかを知っておきます。
+
Django 開発環境の設定
+
Django がどのようなものか知ったので、次は Windows、Linux (Ubuntu)、Mac OS X でDjango 開発環境をセットアップしてテストする方法を説明します。一般的なオペレーティングシステムを使っていれば、この記事で Django アプリケーションの開発を始められるでしょう。
+
Django チュートリアル: 地域図書館ウェブサイト
+
最初の記事は、実践的なチュートリアルで学習する内容を説明し、「地域図書館ウェブサイト」の概要を示します。このウェブサイトの例は、後の記事で作り進めていきます。
+
Django チュートリアル Part 2: ウェブサイトの骨組み作成
+
この記事は、基本的なウェブサイトプロジェクトの「骨組み」をどのように作っていくのかを示します。サイト固有の設定、URL、モデル、ビュー、テンプレートを作成する方法について説明します。
+
Django チュートリアル Part 3: モデルの使用
+
この記事は、地域図書館ウェブサイト用のモデルを定義する方法を示します ― モデルはアプリケーションのデータを格納するデータ構造を表し、Django はデータベースにデータを格納できます。モデルとは何か、どのように定義されるのか、および主要なフィールドタイプのいくつかを説明します。また、モデルデータにアクセスする主な方法のいくつかを簡単に示します。
+
Django チュートリアル Part 4: Django 管理サイト
+
地域図書館ウェブサイトのモデルを作成したので、Django 管理サイトを使用して実際の書籍データを追加します。最初に、管理サイトにモデルを登録する方法を示します。その後、ログインしてデータを作成する方法を示します。最後に、管理サイトの表示を改善する方法をいくつか示します。
+
Django チュートリアル Part 5: ホームページの作成
+
最初のページ全体を表示するコードを追加する準備が整いました。ここでは地域図書館ウェブサイトのホームページで各モデルタイプのレコード数を表示し、他のページへのサイドバーナビゲーションリンクを提供します。基本的な URL マップやビューを作成したり、データベースからレコードを取得したり、テンプレートを使用して実践的な経験を積むことができます。
+
Django チュートリアル Part 6: 汎用の一覧表示と詳細表示
+
このチュートリアルでは、地域図書館ウェブサイトを拡張し、書籍や著者の一覧と詳細ページを追加します。ここでは、汎用のクラスベースのビューについて学び、共通のユースケースのために書くコードの量をどのように減らせるかを示します。またURLのパターンマッチングがどのように行われるのか詳しく説明します。
+
Django チュートリアル Part 7: セッションフレームワーク
+
このチュートリアルでは、地域図書館ウェブサイトを拡張し、ホームページにセッションベースのアクセスカウンターを追加します。これは比較的簡単な例ですが、セッションフレームワークを使用して、自分のサイトの匿名ユーザーに永続的な動作を提供する方法を示しています。
+
Django チュートリアル Part 8: ユーザー認証と権限
+
このチュートリアルでは、ユーザーが自分のアカウントで自分のサイトにログインできるようにする方法と、ログインできるかどうかを制御する方法、ログインしているかどうか、アクセス許可によって表示する方法について説明します。このデモンストレーションの一環として、地域図書館ウェブサイトを拡張し、ログインページとログアウトページを追加し、貸し出された本を表示するためのユーザーとスタッフのページを追加します。
+
Django チュートリアル Part 9: フォームの操作
+
このチュートリアルでは、Django で HTML フォームを扱う方法、特にモデルインスタンスを作成、更新、削除するためのフォームを書く最も簡単な方法を紹介します。このデモンストレーションの一環として、図書館員が書籍を更新したり、管理アプリケーションを使用するのではなく独自のフォームを使用して著者の作成、更新、削除を行えるように、地域図書館ウェブサイトを拡張します。
+
Django チュートリアル Part 10: Django ウェブアプリケーションのテスト
+
ウェブサイトが成長するにつれて、手動でテストするのが困難になります。コンポーネント間のやりとりが複雑になるにつれて、ある領域の小さな変更が、他の領域へ影響がないか検証するためのテストの追加が多く必要になります。これらの問題を軽減する1つの方法は、変更を行うたびに簡単かつ確実に実行できる自動テストを作成することです。このチュートリアルでは、Django のテストフレームワークを使用して、あなたのウェブサイトの単体テストを自動化する方法を示します。
+
Django チュートリアル Part 11: Django を本番環境にデプロイする
+
これで、地域図書館の素晴らしいウェブサイトを作成 (およびテスト) できました。公開ウェブサーバーにインストールして、図書館のスタッフやメンバーがインターネット経由でアクセスできるようにしたいですね。この記事では、ウェブサイトを展開するホストを見つける方法と、サイトを実稼働のために必要な準備作業の概要について説明します。
+
Django ウェブアプリケーションセキュリティ
+
ユーザーデータの保護は、ウェブサイトの設計において不可欠な要素です。以前、ウェブセキュリティの記事でより一般的なセキュリティ上の脅威のいくつかについて説明しました。この記事では、Django の組み込み保護がそのような脅威をどのように処理するかを実践的に実演します。
+
+ +

課題

+ +

次の課題では、上記のガイドに記載されているように、Django を使用してウェブサイトを作成する方法を理解しているかテストします。

+ +
+
DIY Django ミニブログ
+
この課題では、このモジュールから学んだ知識の一部を使用して自分のブログを作成します。
+
diff --git a/files/ja/learn/server-side/django/introduction/index.html b/files/ja/learn/server-side/django/introduction/index.html new file mode 100644 index 0000000000..882c54bb56 --- /dev/null +++ b/files/ja/learn/server-side/django/introduction/index.html @@ -0,0 +1,281 @@ +--- +title: Djangoの紹介 +slug: Learn/Server-side/Django/Introduction +tags: + - Python + - Webフレームワーク + - django + - サーバーサイドプログラミング +translation_of: Learn/Server-side/Django/Introduction +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
+ +

最初のDjangoの記事では、 "Djangoとは何ですか?" という疑問に答え、このWebフレームワークの特徴と概要を説明します。主な機能の概要と、このモジュールで詳しく説明しない高度な機能などを紹介します。 また、Djangoアプリケーションの主要な構成部品のいくつかを示します。 (この時点ではまだテストできる開発環境を持っていません).

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシーを持っていること。サーバーサイドウェブプログラミング の一般的な理解、特にウェブサイトにおけるクライントとサーバーのやりとりの仕組みを理解していること。
目的:Djangoが何であるか、Djangoの持つ機能、そしてDjangoアプリケーションの主要な構成部品に精通します。
+ +

Djangoとは何ですか?

+ +

Djangoは、安全でメンテナンス可能なWebサイトの迅速な開発を可能にする、高度なPython Webフレームワークです。経験豊富な開発者によって開発されたDjangoは、Web開発の多くの面倒事を引き受けてくれます。そのため車輪の再発明が不要で、アプリケーションを書くことに集中できます。無料でオープンソースであり、活発なコミュニティ、偉大なドキュメントがあります。また、無料と有料のサポートの多くのオプションもあります。 

+ +

Djangoの以下の特徴はソフトウェアを書くのに役立ちます:

+ +
+
完全
+
Djangoは "Batteries included" の哲学に従い、開発者が "すぐに" やりたいことのほとんどを提供します。必要なものはすべて1つの「製品」に含まれているため、すべてがシームレスに連携し、一貫した設計原則に従います。そして、豊富な最新のドキュメントが用意されています。
+
多彩
+
Djangoは、コンテンツ管理システムやWikiからソーシャルネットワーク、ニュースサイトなど、ほとんどのタイプのWebサイトを構築できます。任意のクライアントサイドのフレームワークで動作し、HTML、RSSフィード、JSON、XMLなどのほとんどの形式のコンテンツを配信できます。現在読んでいるサイトもDjangoベースですよ!
+
+ 内部的には、必要となるほとんどの機能(人気のあるデータベースやテンプレートエンジンなど)の選択肢を提供していますが、他のコンポーネントを使用するように拡張もできます。
+
安全
+
Djangoは、Webサイトを自動的に保護するために "正しいことをする" ように設計されたフレームワークです。多くの一般的なセキュリティミスを開発者が避けられるようにできています。例えば、セッション情報を脆弱な場所に置いたり、パスワードを直接保存するといった一般的なミスをDjangoでは避けるようになっています。セッション情報は、クッキーにセッションキーだけを格納し、実際のセッションデータはデータベースに保存されます。パスワードはパスワードハッシュをデータベースに格納します。このようにユーザーアカウントとパスワードを安全に管理する方法を提供しています。
+
+ パスワードハッシュは、送信されたパスワードから 暗号化ハッシュ関数を介して生成された固定長の値です。 Djangoは入力されたパスワードが正しいかどうかを、ハッシュ関数を介した値と保存されたハッシュ値を比較することでチェックできます。これは "一方向性" の機能であり、もし保存されているハッシュ値が侵害されても、攻撃者が元のパスワードを解読するのは困難です。
+
+ Djangoは、SQLインジェクション、クロスサイトスクリプティング(XSS)、クロスサイトリクエストフォージェリ(CSRF)、クリックジャッキングなどの多くの脆弱性に対する保護が有効です(これらの攻撃についての詳細は、 ウェブサイトのセキュリティ を参照してください)。
+
スケーラブル
+
Djangoはコンポーネントベースの “シェアードナッシング” アーキテクチャを採用しています(アーキテクチャの各部分は他と独立しており、必要に応じて置き換え、変更できます)。異なる部分を明確に分離しているため、キャッシュサーバー、データベースサーバー、アプリケーションサーバーの各ハードウェアをそれぞれ追加することによって、トラフィックの増加に合わせてスケールできるようになっています。いくつかの最も忙しいサイトは、ニーズを満たすためにDjangoを適切にスケールさせています(InstagramやDisqusなど)
+
メンテナンス可能
+
Djangoのコードは、保守可能で再利用可能になるような設計原則、デザインパターンを使って書かれています。特に、Do not Repeat Yourself(DRY)原則によって不要な複製がなく、コード量を削減します。Djangoは、関連する機能を再利用可能な "アプリケーション" にグループ化し、低いレベルでは関連するコードをモジュールにグループ化します(モデルビューコントローラ(MVC)パターンに沿っています)。
+
ポータブル
+
Djangoのコードは多くのプラットフォームで動作するPythonで書かれています。これはあなたがプラットフォームに縛られていないことを意味します。アプリケーションは多くの種類のLinux、Windows、Mac OS Xで実行できます。さらに、Djangoは多くのホスティングプロバイダによってよくサポートされています。Djangoサイトを特定のインフラストラクチャでホスティングするために、ドキュメントを提供していることが多いです。
+
+ +

どこから来たの?

+ +

Djangoは当初、2003年から2005年の間に、新聞のウェブサイトの作成とメンテナンスを担当するWebチームによって開発されました。いくつかのサイトを作成した後、チームは多くの共通コードとデザインパターンを除外、再利用するようになりました。この共通コードは、"Django" プロジェクトとして2005年7月にオープンソース化され、汎用のWeb開発フレームワークに発展しました。 

+ +

Djangoは、2008年9月の最初のマイルストーンリリース(1.0)から、最新のリリースバージョンである2.0(2017)まで、成長を続けています。 各リリースでは、新しいタイプのデータベース、テンプレートエンジン、キャッシュのサポートから、"汎用"ビュー機能とクラスの追加(プログラミングタスクで開発者が記述しなければならないコード量を削減します)などの新機能追加やバグフィックスがありました。 

+ +
+

ノート: DjangoのWebサイトの リリースノート をチェックして、最近のバージョンで何が変わったのか、どのような作業がDjangoを改善しているのか確認してください。

+
+ +

Djangoは現在、活発なオープンソースプロジェクトであり、何千人ものユーザーとコントリビュータがいます。元々の起源に関連するいくつかの機能はまだありますが、DjangoはあらゆるタイプのWebサイトを開発できる汎用フレームワークに進化しました。 

+ +

Djangoはどれくらい普及していますか?

+ +

サーバーサイドのフレームワークの普及率を決定的に測定する、すぐに利用できるものはありません (Hot Frameworks のようなサイトでは、GitHubプロジェクトの数や、各プラットフォームのStackOverflowの質問数をカウントするなどのメカニズムを使用して人気を評価しています) より良い質問は、「人気のないプラットフォームで問題を避けるために、Djangoは "人気がある" かどうか」、「それは進化し続けていますか?」「必要なときに助けを得ることができますか?」「Djangoを学べば、仕事を得る機会がありますか?」などです。

+ +

Djangoを使用している有名なサイトの数、コードベースにコントリビュートする人数、無料と有料の両方でサポートを提供する人数に基づいて、Djangoは普及しているフレームワークと言えるでしょう。

+ +

Djangoを使用している有名なサイト: Disqus、Instagram、Knight Foundation、MacArthur Foundation、Mozilla、National Geographic、Open Knowledge Foundation、Pinterest、Open Stack (ソース: Djangoホームページ)

+ +

Djangoはこだわりが強いですか?

+ +

Webフレームワークは、自身を "こだわりが強い" か "こだわりが強くない" と呼ぶことがあります。

+ +

こだわりが強いフレームワークは、特定のタスクを処理するための "正しい方法" についてこだわりを持っています。特定のドメイン(特定の種類の問題の解決)は、どのように扱うと正しいか、広く知られており、文書化もされているため、迅速な開発をサポートします。 しかし、主ドメイン以外の問題解決において柔軟性を欠き、使用できるコンポーネントのやアプローチの選択肢が少なくなる傾向があります。

+ +

対照的に、こだわりの強くないフレームワークは、目的を達成するための最善の結合されたコンポーネントの提供や、使用するコンポーネントを制限することが非常に少ないです。開発者は、特定のタスクを完了するために最も適したツールを簡単に使用できます。ただし、それらのコンポーネントを自分で見つける必要があります。
+
+ Djangoは "多少こだわりがある" ので、 "両方の世界のベスト" を提供します。 ほとんどのWeb開発タスクを処理するための一連のコンポーネントと、それを使う1つ(または2つ)の正しい方法を提供します。Djangoの分離アーキテクチャでは、通常、さまざまなオプションから選択しますが、必要に応じて新しいオプションを追加できます。

+ +

Djangoコードはどのように見えますか?

+ +

昔ながらのデータ駆動型Webサイトでは、WebアプリケーションはWebブラウザ(または他のクライアント)からのHTTPリクエストを待ちます。リクエストを受信すると、アプリケーションは、URLおよび、GET や POST データの情報に基づいて必要な処理を行います。 必要に応じてデータベースの情報を読み書きしたり、要求を満たすために必要なタスクを実行できます。 アプリケーションはWebブラウザに応答を返す際、検索したデータをHTMLテンプレートのプレースホルダに挿入し、動的にHTMLページを生成することがよくあります。

+ +

Django Webアプリケーションは、通常、これらの各ステップを処理するコードを別々のファイルにグループ化します:

+ +

+ + + +
+

ノート: Djangoはこの構成を "モデルビューテンプレート(Model View Template, MVT)" アーキテクチャと呼んでいます。これは Model View Controller アーキテクチャとよく似ています。 

+
+ + + +

以下のセクションでは、Djangoアプリケーションの主要部分の外観について説明します(開発環境を設定後、詳しく説明します)。

+ +

リクエストを正しいビューに送信する(urls.py)

+ +

URLマッパーは通常、urls.pyという名前のファイルに格納されます。以下の例では、マッパー(urlpatterns)はルート(特定のURLパターン) と対応するビュー関数のマッピングのリストを定義します。指定されたパターンと一致するURLを持つHTTPリクエストが受信されると、関連するビュー関数が呼び出され、リクエストを渡します。

+ +
urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('book/<int:id>/', views.book-detail, name='book_detail'),
+    path('catalog/', include('catalog.urls')),
+    re_path(r'^([0-9]+)/$', views.best),
+]
+
+ +

 

+ +

urlpatternsオブジェクトはpath()re_path()関数のリストです(Pythonのリストは角括弧を使って定義され、アイテムはカンマ区切りです。末尾のカンマは任意です。例:  [item1, item2, item3,]

+ +

両方の関数の最初の引数は、一致させたいルート(パターン)です。path() 関数では、かぎ括弧を使って、ビュー関数に名前付き引数として渡されるURLの部分を定義します。 re_path() 関数では、柔軟なパターンマッチング方法として知られている正規表現を使います。 これらについては後の記事で説明します。

+ +

2つ目の引数は、パターンに一致したときに呼び出される別の関数です。views.book_detailという表記は、呼び出されたviewsモジュール内(views.pyという名前のファイル内部)で見つかるbook_detail()関数が呼び出されることを示します。

+ +

 

+ +

リクエストの処理(views.py)

+ +

ビューは、Webアプリケーションの中心であり、WebクライアントからHTTPリクエストを受け取り、HTTPレスポンスを返します。その中では、データベースにアクセスしたり、テンプレートをレンダリングしたりして、フレームワークの他のリソースを変換します。

+ +

以下の例は、前のセクションのURLマッパーによって呼び出される最小限の index() ビュー関数を示しています。すべてのビュー関数と同様に、HttpRequestオブジェクトをパラメータ(request)として受け取り、HttpResponseオブジェクトを返却します。この場合、リクエストには何もしません。レスポンスはハードコードされた文字列を返します。後のセクションで、もっと興味深いことをするリクエストを示します。

+ +
## filename: views.py (Django view functions)
+
+from django.http import HttpResponse
+
+def index(request):
+    # Get an HttpRequest - the request parameter
+    # perform operations using information from the request.
+    # Return HttpResponse
+    return HttpResponse('Hello from Django!')
+
+ +
+

ノート: Pythonについて少し:

+ + +
+ + + +

ビューは通常 views.py ファイルに格納します。

+ +

データモデルの定義(models.py)

+ +

Django Webアプリケーションは、モデルと呼ばれるPythonオブジェクトを通じてデータを管理し、クエリを実行します。モデルは、フィールドのタイプや、最大サイズ、デフォルト値、選択リストのオプション、ドキュメントのヘルプテキスト、フォームのラベルテキストなど、格納されるデータの構造を定義します。モデルの定義は、下層のデータベース(プロジェクト設定でいくつかから選択できます)からは独立しています。使用したいデータベースを選択したあとは、直接そのデータベースとやりとりする必要はありません。モデル構造と他のコードを書くだけで、Djangoはデータベースとのやりとりのすべての面倒な作業を処理します。

+ +

以下のコードスニペットは、Teamオブジェクトの非常に単純なDjangoモデルを示しています。Teamクラスは、Djangoのmodels.Modelクラスを継承しています。これは、チーム名とチームレベルを文字列フィールドとして定義し、各レコードに格納する最大文字数を指定します。team_levelはいくつかの値の一つになる可能性があります。したがって、この値を選択フィールドとして定義し、表示する選択項目と保存するデータの間のマッピングをデフォルト値とともに提供します。

+ +
# filename: models.py
+
+from django.db import models
+
+class Team(models.Model):
+    team_name = models.CharField(max_length=40)
+
+    TEAM_LEVELS = (
+        ('U09', 'Under 09s'),
+        ('U10', 'Under 10s'),
+        ('U11', 'Under 11s'),
+        ...  #list other team levels
+    )
+    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
+
+ +
+

ノート: Pythonについて少し:

+ + +
+ +

データの問い合わせ(views.py)

+ +

Djangoモデルは、データベースを検索するための簡単なクエリAPIを提供します。これは、さまざまな照合基準(例:完全一致、大文字小文字を区別しない、大なりなど)を使用して一度にいくつかのフィールドと照合でき、複雑なステートメントをサポートできます(たとえば、「U11チームのうち、"Fr"で始まるか"al"で終わる名前」)。

+ +

コードスニペットには、U09チームのすべてを表示するためのビュー機能(リソースハンドラ)が示されています。太字の線は、モデルクエリAPIを使用して、team_level フィールドに完全一致でテキスト 'U09' があるすべてのレコードをフィルタリングする方法を示しています。 (filter() 関数には、照合基準としてフィールド名とマッチタイプをダブルアンダースコアで区切った引数で渡していることに注意してください: team_level__exact)

+ +
## filename: views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def index(request):
+    list_teams = Team.objects.filter(team_level__exact="U09")
+    context = {'youngest_teams': list_teams}
+    return render(request, '/best/index.html', context)
+
+ +
+
+ +

この関数は、ブラウザに返信されるHttpResponseを生成するために render() 関数を使います。この関数はショートカットです。指定されたHTMLテンプレートとテンプレートに挿入するいくつかのデータ("context"という名前の変数で提供される)を組み合わせてHTMLファイルを作成します。次のセクションでは、HTMLを作成するためにテンプレートにデータがどのように挿入されているかを示します。

+ +

データのレンダリング(HTMLテンプレート)

+ +

テンプレートシステムでは、出力ドキュメントの構造を指定できます。ページの生成時に埋められるデータはプレースホルダを使用します。テンプレートはHTMLを作成するためによく使われますが、他のタイプドキュメントも作成できます。Djangoはネイティブのテンプレートシステムと、Jinja2と呼ばれる一般的なPythonライブラリの両方をサポートしています。(必要に応じて他のシステムもサポートできます)

+ +

コードスニペットは、前のセクションの render()関数で呼び出されるHTMLテンプレートの外観を示しています。 このテンプレートは、レンダリング時にyoungest_teams というリスト変数(上記のrender()関数によってコンテキスト変数に含まれます)にアクセスできる前提で作成されています。HTMLスケルトンでは、youngest_teams変数が存在するかどうかを最初にチェックし、それを forループで繰り返す式があります。各繰り返しにおいて、テンプレートは各チームのteam_name値を{{htmlelement("li")}}要素に表示します。

+ +
## filename: best/templates/best/index.html
+
+<!DOCTYPE html>
+<html lang="en">
+<body>
+
+ {% if youngest_teams %}
+    <ul>
+    {% for team in youngest_teams %}
+        <li>\{\{ team.team_name \}\}</li>
+    {% endfor %}
+    </ul>
+{% else %}
+    <p>No teams are available.</p>
+{% endif %}
+
+</body>
+</html>
+ +

他に何ができますか?

+ +

前のセクションでは、ほとんどのWebアプリケーションで使用する主な機能(URLマッピング、ビュー、モデルおよびテンプレート)を示しました。Djangoが提供する他の機能のいくつかを紹介します:

+ + + +

要約

+ +

おめでとう、あなたはDjangoの旅の最初のステップを完了しました!Djangoの主なメリット、歴史について少し分かって、Djangoアプリケーションの主要部分のそれぞれがどのように見えるかを理解する必要があります。リスト、関数、クラスの構文も含めて、Pythonプログラミング言語についていくつか学んだことがあります。

+ +

上記では実際のDjangoコードをいくつか見ましたが、クライアントサイドのコードとは異なり、実行するための開発環境をセットアップする必要があります。それが次のステップです。

+ +
{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
+ +

このモジュール内

+ + diff --git a/files/ja/learn/server-side/django/models/index.html b/files/ja/learn/server-side/django/models/index.html new file mode 100644 index 0000000000..fb3952f6b9 --- /dev/null +++ b/files/ja/learn/server-side/django/models/index.html @@ -0,0 +1,461 @@ +--- +title: 'Django チュートリアル Part 3: モデルの使用' +slug: Learn/Server-side/Django/Models +translation_of: Learn/Server-side/Django/Models +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}
+ +

この記事では、LocalLibrary Web サイトのモデルを定義する方法を説明します。モデルとは何か、その宣言方法、および主要なフィールドタイプについて説明します。また、モデルデータにアクセスするための主な方法のいくつかについても簡単に説明します。

+ + + + + + + + + + + + +
前提条件:Django チュートリアル Part 2: Web サイトの骨組み作成.
目標:適切なフィールドを選択しながら、独自のモデルを設計および作成できるようになる。
+ +

概要

+ +

Django web applications access and manage data through Python objects referred to as models. Models define the structure of stored data, including the field types and possibly also their maximum size, default values, selection list options, help text for documentation, label text for forms, etc. The definition of the model is independent of the underlying database — you can choose one of several as part of your project settings. Once you've chosen what database you want to use, you don't need to talk to it directly at all — you just write your model structure and other code, and Django handles all the dirty work of communicating with the database for you.

+ +

This tutorial shows how to define and access the models for the LocalLibrary website example.

+ +

Designing the LocalLibrary models

+ +

Before you jump in and start coding the models, it's worth taking a few minutes to think about what data we need to store and the relationships between the different objects.

+ +

We know that we need to store information about books (title, summary, author, written language, category, ISBN) and that we might have multiple copies available (with globally unique id, availability status, etc.). We might need to store more information about the author than just their name, and there might be multiple authors with the same or similar names. We want to be able to sort information based on book title, author, written language, and category.

+ +

When designing your models it makes sense to have separate models for every "object" (a group of related information). In this case, the obvious objects are books, book instances, and authors.

+ +

You might also want to use models to represent selection-list options (e.g. like a drop down list of choices), rather than hard coding the choices into the website itself — this is recommended when all the options aren't known up front or may change. Obvious candidates for models, in this case, include the book genre (e.g. Science Fiction, French Poetry, etc.) and language (English, French, Japanese).

+ +

Once we've decided on our models and field, we need to think about the relationships. Django allows you to define relationships that are one to one (OneToOneField), one to many (ForeignKey) and many to many (ManyToManyField).

+ +

With that in mind, the UML association diagram below shows the models we'll define in this case (as boxes).

+ +

LocalLibrary Model UML

+ +

We've created models for the book (the generic details of the book), book instance (status of specific physical copies of the book available in the system), and author. We have also decided to have a model for the genre so that values can be created/selected through the admin interface. We've decided not to have a model for the BookInstance:status — we've hardcoded the values (LOAN_STATUS) because we don't expect these to change. Within each of the boxes, you can see the model name, the field names, and types, and also the methods and their return types.

+ +

The diagram also shows the relationships between the models, including their multiplicities. The multiplicities are the numbers on the diagram showing the numbers (maximum and minimum) of each model that may be present in the relationship. For example, the connecting line between the boxes shows that Book and a Genre are related. The numbers close to the Genre model show that a book must have one or more Genres (as many as you like), while the numbers on the other end of the line next to the Book model show that a Genre can have zero or many associated books.

+ +
+

Note: The next section provides a basic primer explaining how models are defined and used. As you read it, consider how we will construct each of the models in the diagram above.

+
+ +

Model primer

+ +

This section provides a brief overview of how a model is defined and some of the more important fields and field arguments.

+ +

Model definition

+ +

Models are usually defined in an application's models.py file. They are implemented as subclasses of django.db.models.Model, and can include fields, methods and metadata. The code fragment below shows a "typical" model, named MyModelName:

+ +
from django.db import models
+
+class MyModelName(models.Model):
+    """A typical class defining a model, derived from the Model class."""
+
+    # Fields
+    my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
+    ...
+
+    # Metadata
+    class Meta:
+        ordering = ['-my_field_name']
+
+    # Methods
+    def get_absolute_url(self):
+        """Returns the url to access a particular instance of MyModelName."""
+        return reverse('model-detail-view', args=[str(self.id)])
+
+    def __str__(self):
+        """String for representing the MyModelName object (in Admin site etc.)."""
+        return self.my_field_name
+ +

In the below sections we'll explore each of the features inside the model in detail:

+ +

Fields

+ +

A model can have an arbitrary number of fields, of any type — each one represents a column of data that we want to store in one of our database tables. Each database record (row) will consist of one of each field value. Let's look at the example seen below:

+ +
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
+ +

Our above example has a single field called my_field_name, of type models.CharField — which means that this field will contain strings of alphanumeric characters. The field types are assigned using specific classes, which determine the type of record that is used to store the data in the database, along with validation criteria to be used when values are received from an HTML form (i.e. what constitutes a valid value). The field types can also take arguments that further specify how the field is stored or can be used. In this case we are giving our field two arguments:

+ + + +

The field name is used to refer to it in queries and templates. Fields also have a label, which is either specified as an argument (verbose_name) or inferred by capitalising the first letter of the field's variable name and replacing any underscores with a space (for example my_field_name would have a default label of My field name).

+ +

The order that fields are declared will affect their default order if a model is rendered in a form (e.g. in the Admin site), though this may be overridden.

+ +
Common field arguments
+ +

The following common arguments can be used when declaring many/most of the different field types:

+ + + +

There are many other options — you can view the full list of field options here.

+ +
Common field types
+ +

The following list describes some of the more commonly used types of fields. 

+ + + +

There are many other types of fields, including fields for different types of numbers (big integers, small integers, floats), booleans, URLs, slugs, unique ids, and other "time-related" information (duration, time, etc.). You can view the full list here.

+ +

Metadata

+ +

You can declare model-level metadata for your Model by declaring class Meta, as shown.

+ +
class Meta:
+    ordering = ['-my_field_name']
+
+ +

One of the most useful features of this metadata is to control the default ordering of records returned when you query the model type. You do this by specifying the match order in a list of field names to the ordering attribute, as shown above. The ordering will depend on the type of field (character fields are sorted alphabetically, while date fields are sorted in chronological order). As shown above, you can prefix the field name with a minus symbol (-) to reverse the sorting order.

+ +

So as an example, if we chose to sort books like this by default:

+ +
ordering = ['title', '-pubdate']
+ +

the books would be sorted alphabetically by title, from A-Z, and then by publication date inside each title, from newest to oldest.

+ +

Another common attribute is verbose_name, a verbose name for the class in singular and plural form:

+ +
verbose_name = 'BetterName'
+ +

Other useful attributes allow you to create and apply new "access permissions" for the model (default permissions are applied automatically), allow ordering based on another field, or to declare that the class is "abstract" (a base class that you cannot create records for, and will instead be derived from to create other models).

+ +

Many of the other metadata options control what database must be used for the model and how the data is stored (these are really only useful if you need to map a model to an existing database).

+ +

The full list of metadata options are available here: Model metadata options (Django docs).

+ +

Methods

+ +

A model can also have methods.

+ +

Minimally, in every model you should define the standard Python class method __str__() to return a human-readable string for each object. This string is used to represent individual records in the administration site (and anywhere else you need to refer to a model instance). Often this will return a title or name field from the model.

+ +
def __str__(self):
+    return self.field_name
+ +

Another common method to include in Django models is get_absolute_url(), which returns a URL for displaying individual model records on the website (if you define this method then Django will automatically add a "View on Site" button to the model's record editing screens in the Admin site). A typical pattern for get_absolute_url() is shown below.

+ +
def get_absolute_url(self):
+    """Returns the url to access a particular instance of the model."""
+    return reverse('model-detail-view', args=[str(self.id)])
+
+ +
+

Note: Assuming you will use URLs like /myapplication/mymodelname/2 to display individual records for your model (where "2" is the id for a particular record), you will need to create a URL mapper to pass the response and id to a "model detail view" (which will do the work required to display the record). The reverse() function above is able to "reverse" your url mapper (in the above case named 'model-detail-view') in order to create a URL of the right format.

+ +

Of course to make this work you still have to write the URL mapping, view, and template!

+
+ +

You can also define any other methods you like, and call them from your code or templates (provided that they don't take any parameters).

+ +

Model management

+ +

Once you've defined your model classes you can use them to create, update, or delete records, and to run queries to get all records or particular subsets of records. We'll show you how to do that in the tutorial when we define our views, but here is a brief summary.

+ +

Creating and modifying records

+ +

To create a record you can define an instance of the model and then call save().

+ +
# Create a new record using the model's constructor.
+record = MyModelName(my_field_name="Instance #1")
+
+# Save the object into the database.
+record.save()
+
+ +
+

Note: If you haven't declared any field as a primary_key, the new record will be given one automatically, with the field name id. You could query this field after saving the above record, and it would have a value of 1.

+
+ +

You can access the fields in this new record using the dot syntax, and change the values. You have to call save() to store modified values to the database.

+ +
# Access model field values using Python attributes.
+print(record.id) # should return 1 for the first record.
+print(record.my_field_name) # should print 'Instance #1'
+
+# Change record by modifying the fields, then calling save().
+record.my_field_name = "New Instance Name"
+record.save()
+ +

Searching for records

+ +

You can search for records that match certain criteria using the model's objects attribute (provided by the base class).

+ +
+

Note: Explaining how to search for records using "abstract" model and field names can be a little confusing. In the discussion below we'll refer to a Book model with title and genre fields, where genre is also a model with a single field name.

+
+ +

We can get all records for a model as a QuerySet, using objects.all(). The QuerySet is an iterable object, meaning that it contains a number of objects that we can iterate/loop through.

+ +
all_books = Book.objects.all()
+
+ +

Django's filter() method allows us to filter the returned QuerySet to match a specified text or numeric field against particular criteria. For example, to filter for books that contain "wild" in the title and then count them, we could do the following.

+ +
wild_books = Book.objects.filter(title__contains='wild')
+number_wild_books = wild_books.count()
+
+ +

The fields to match and the type of match are defined in the filter parameter name, using the format: field_name__match_type (note the double underscore between title and contains above). Above we're filtering title with a case-sensitive match. There are many other types of matches you can do: icontains (case insensitive), iexact (case-insensitive exact match), exact (case-sensitive exact match) and in, gt (greater than), startswith, etc. The full list is here.

+ +

In some cases you'll need to filter on a field that defines a one-to-many relationship to another model (e.g. a ForeignKey). In this case you can "index" to fields within the related model with additional double underscores. So for example to filter for books with a specific genre pattern, you will have to index to the name through the genre field, as shown below:

+ +
# Will match on: Fiction, Science fiction, non-fiction etc.
+books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')
+
+ +
+

Note: You can use underscores (__) to navigate as many levels of relationships (ForeignKey/ManyToManyField) as you like. For example, a Book that had different types, defined using a further "cover" relationship might have a parameter name: type__cover__name__exact='hard'.

+
+ +

There is a lot more you can do with queries, including backwards searches from related models, chaining filters, returning a smaller set of values etc. For more information see Making queries (Django Docs).

+ +

Defining the LocalLibrary Models

+ +

In this section we will start defining the models for the library. Open models.py (in /locallibrary/catalog/). The boilerplate at the top of the page imports the models module, which contains the model base class models.Model that our models will inherit from.

+ +
from django.db import models
+
+# Create your models here.
+ +

Genre model

+ +

Copy the Genre model code shown below and paste it into the bottom of your models.py file. This model is used to store information about the book category — for example whether it is fiction or non-fiction, romance or military history, etc. As mentioned above, we've created the Genre as a model rather than as free text or a selection list so that the possible values can be managed through the database rather than being hard coded.

+ +
class Genre(models.Model):
+    """Model representing a book genre."""
+    name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
+
+    def __str__(self):
+        """String for representing the Model object."""
+        return self.name
+ +

The model has a single CharField field (name), which is used to describe the genre (this is limited to 200 characters and has some help_text. At the end of the model we declare a __str__() method, which simply returns the name of the genre defined by a particular record. No verbose name has been defined, so the field will be called Name in forms.

+ +

Book model

+ +

Copy the Book model below and again paste it into the bottom of your file. The book model represents all information about an available book in a general sense, but not a particular physical "instance" or "copy" available for loan. The model uses a CharField to represent the book's title and isbn (note how the isbn specifies its label as "ISBN" using the first unnamed parameter because the default label would otherwise be "Isbn"). The model uses TextField for the summary, because this text may need to be quite long.

+ +
from django.urls import reverse # Used to generate URLs by reversing the URL patterns
+
+class Book(models.Model):
+    """Model representing a book (but not a specific copy of a book)."""
+    title = models.CharField(max_length=200)
+
+    # Foreign Key used because book can only have one author, but authors can have multiple books
+    # Author as a string rather than object because it hasn't been declared yet in the file
+    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
+
+    summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
+    isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
+
+    # ManyToManyField used because genre can contain many books. Books can cover many genres.
+    # Genre class has already been defined so we can specify the object above.
+    genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
+
+    def __str__(self):
+        """String for representing the Model object."""
+        return self.title
+
+    def get_absolute_url(self):
+        """Returns the url to access a detail record for this book."""
+        return reverse('book-detail', args=[str(self.id)])
+
+ +

The genre is a ManyToManyField, so that a book can have multiple genres and a genre can have many books. The author is declared as ForeignKey, so each book will only have one author, but an author may have many books (in practice a book might have multiple authors, but not in this implementation!)

+ +

In both field types the related model class is declared as the first unnamed parameter using either the model class or a string containing the name of the related model. You must use the name of the model as a string if the associated class has not yet been defined in this file before it is referenced! The other parameters of interest in the author field are null=True, which allows the database to store a Null value if no author is selected, and on_delete=models.SET_NULL, which will set the value of the author to Null if the associated author record is deleted.

+ +

The model also defines __str__() , using the book's title field to represent a Book record. The final method, get_absolute_url() returns a URL that can be used to access a detail record for this model (for this to work we will have to define a URL mapping that has the name book-detail, and define an associated view and template).

+ +

BookInstance model

+ +

Next, copy the BookInstance model (shown below) under the other models. The BookInstance represents a specific copy of a book that someone might borrow, and includes information about whether the copy is available or on what date it is expected back, "imprint" or version details, and a unique id for the book in the library.

+ +

Some of the fields and methods will now be familiar. The model uses

+ + + +
import uuid # Required for unique book instances
+
+class BookInstance(models.Model):
+    """Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
+    id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
+    book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
+    imprint = models.CharField(max_length=200)
+    due_back = models.DateField(null=True, blank=True)
+
+    LOAN_STATUS = (
+        ('m', 'Maintenance'),
+        ('o', 'On loan'),
+        ('a', 'Available'),
+        ('r', 'Reserved'),
+    )
+
+    status = models.CharField(
+        max_length=1,
+        choices=LOAN_STATUS,
+        blank=True,
+        default='m',
+        help_text='Book availability',
+    )
+
+    class Meta:
+        ordering = ['due_back']
+
+    def __str__(self):
+        """String for representing the Model object."""
+        return f'{self.id} ({self.book.title})'
+ +

We additionally declare a few new types of field:

+ + + +

The model __str__() represents the BookInstance object using a combination of its unique id and the associated Book's title.

+ +
+

Note: A little Python:

+ + +
+ +

Author model

+ +

Copy the Author model (shown below) underneath the existing code in models.py.

+ +
class Author(models.Model):
+    """Model representing an author."""
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    date_of_birth = models.DateField(null=True, blank=True)
+    date_of_death = models.DateField('Died', null=True, blank=True)
+
+    class Meta:
+        ordering = ['last_name', 'first_name']
+
+    def get_absolute_url(self):
+        """Returns the url to access a particular author instance."""
+        return reverse('author-detail', args=[str(self.id)])
+
+    def __str__(self):
+        """String for representing the Model object."""
+        return f'{self.last_name}, {self.first_name}'
+
+ +

All of the fields/methods should now be familiar. The model defines an author as having a first name, last name, and dates of birth and death (both optional). It specifies that by default the __str__() returns the name in last name, firstname order. The get_absolute_url() method reverses the author-detail URL mapping to get the URL for displaying an individual author.

+ +

Re-run the database migrations

+ +

All your models have now been created. Now re-run your database migrations to add them to your database.

+ +
python3 manage.py makemigrations
+python3 manage.py migrate
+ +

Language model — challenge

+ +

Imagine a local benefactor donates a number of new books written in another language (say, Farsi). The challenge is to work out how these would be best represented in our library website, and then to add them to the models.

+ +

Some things to consider:

+ + + +

After you've decided, add the field. You can see what we decided on Github here.

+ +

Don't forget that after a change to your model, you should again re-run your database migrations to add the changes.

+ +
python3 manage.py makemigrations
+python3 manage.py migrate
+ + + + + +

Summary

+ +

In this article we've learned how models are defined, and then used this information to design and implement appropriate models for the LocalLibrary website.

+ +

At this point we'll divert briefly from creating the site, and check out the Django Administration site. This site will allow us to add some data to the library, which we can then display using our (yet to be created) views and templates.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}

+ + + +

In this module

+ + diff --git a/files/ja/learn/server-side/django/skeleton_website/index.html b/files/ja/learn/server-side/django/skeleton_website/index.html new file mode 100644 index 0000000000..0d76ae46eb --- /dev/null +++ b/files/ja/learn/server-side/django/skeleton_website/index.html @@ -0,0 +1,398 @@ +--- +title: 'Django チュートリアル Part 2: スケルトンウェブサイトの作成' +slug: Learn/Server-side/Django/skeleton_website +tags: + - django + - イントロダクション + - ガイド + - チュートリアル + - 初心者 + - 学習 + - 記事 +translation_of: Learn/Server-side/Django/skeleton_website +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}
+ +

Djangoチュートリアル の2つ目の記事では、基本的なウェブサイトプロジェクトの「スケルトン」をどのように作っていくのかを説明します。サイト固有の設定、URL、モデル、ビュー、テンプレートを作成する方法について説明します。

+ + + + + + + + + + + + +
前提条件:Django 開発環境の設定Djangoチュートリアルを確認してください。
目的:Djangoのツールを使って自分の新しいウェブサイトプロジェクトを開始できるようにする。
+ +

概要

+ +

この記事は、"スケルトン"ウェブサイトを作る方法を示します。そこにはサイト固有の設定、パス、モデル、ビューやテンプレートを組み込むことができるます。(これらについては後で述べます)

+ +

そのプロセスは単純です:

+ +
    +
  1. django-admin ツールを使ってプロジェクトフォルダ、基本的なテンプレートファイル、プロジェクト管理スクリプト(manage.py)を作ります .
  2. +
  3. manage.py は1つ以上のアプリケーションを作ります。 +
    +

    メモ: ウェブサイトは1つ以上のセクションから成ります。例えば、メインサイト、ブログ、ウィキ、ダウンロードエリアなど。Djangoは、これらのコンポーネントを別々のアプリケーションとして作成することを助けてくれます。それらは、必要なら異なるプロジェクトで再利用できます。

    +
    +
  4. +
  5. プロジェクトにアプリケーションを含めるために登録します。
  6. +
  7. url/path マッパーはそれらのアプリケーションを結びつけます。
  8. +
+ +

Local Library website のために、ウェブサイトフォルダとプロジェクトフォルダはlocallibrary という名前をつけます。また、1つのアプリケーションはcatalogという名前をつけます。 したがって、最上位のフォルダ構成は以下のようになります。:

+ +
locallibrary/         # Website foldermanage.py         # Script to run Django tools for this project (created using django-admin)
+    locallibrary/     # Website/project folder (created using django-admin)
+    catalog/          # Application folder (created using manage.py)
+
+ +

以下のセクションでは、その過程をもっと詳細に述べ、あなたが変更を試す方法を示しましょう。この記事の最後に、我々はいくつかの他のウェブサイトの設定について述べてみます。それはあなたがこのステージで行っていることかもしませんが。

+ +

プロジェクトの作成

+ +

始めにコマンドプロンプトまたはターミナルを開いて、(先に自分が仮想環境(virtual environment)にいることを確認して下さい)、Djangoアプリを格納したい場所へ移動します(ドキュメントフォルダの中など探しやすい場所にしましょう)。そして、新しいウェブサイトのフォルダ(この場合は locallibraryを作りましょう。そして、cdコマンドでそのフォルダへ移動しましょう。

+ +
mkdir locallibrary
+cd locallibrary
+ +

以下のように、django-admin startprojectコマンドで新しいプロジェクトを作り、そのフォルダの中に移動します。

+ +
django-admin startproject locallibrary
+cd locallibrary
+ +

django-adminツールは以下のようなフォルダ/ファイル構成を作ります。

+ +
locallibrary/manage.pylocallibrary/
+        settings.py
+        urls.py
+        wsgi.py
+ +

我々の現在の作業ディレクトリはこのようなものになっているでしょう。

+ +
../django_projects/locallibrary/
+ +

locallibraryプロジェクトのサブフォルダはこのウェブサイトに入口点となります: 

+ + + +

manage.py スクリプトはアプリケーションを作成したり、データベースを操作したり、webサーバを起動したりするのに使われます。

+ +

catalogアプリケーションの作成

+ +

次に、以下のコマンドを実行し、localibrary プロジェクトの中にアプリケーションを作りましょう。(このコマンドはプロジェクト内のmanage.pyと同じフォルダで実行する必要があります)

+ +
python3 manage.py startapp catalog
+ +
+

メモ: 上記コマンドは LinuxやmacOS X用です。Windows のコマンドは: py -3 manage.py startapp catalog

+ +

もしWindowsを使っているなら、このモジュール(manage.py)を使う際はpython3py -3 に変更して下さい。

+ +

もしPython 3.7.0以降を使用しているなら、py manage.py startapp catalogで使用できます。

+
+ +

このツールは新しいフォルダを作成し、アプリケーションの様々なパーツとなるファイルをそのフォルダに追加します(以下の太字で表示)。 ほとんどのファイルは目的に応じて便利な名前が付けられており(例えば ビューはviews.pyに、モジュールは models.pyに、テストはtests.pyに、管理サイトの設定は admin.pyに、アプリケーションの登録はapps.pyに保存する必要がある)、 さらに、関連するオブジェクトを操作するための最低限の定型的なコードを用意している。

+ +

アップデートされた後のプロジェクトディレクトリはこのようになる:

+ +
locallibrary/
+    manage.py
+    locallibrary/
+    catalog/
+        admin.py
+        apps.py
+        models.py
+        tests.py
+        views.py
+        __init__.py
+        migrations/
+
+ +

加えて今持っているものは:

+ + + +
+

メモ: 上記ファイルリストに何か欠けているものがあることに気づきましたか? ビューやモデルがある一方で、URLマッピング、テンプレート、静的ファイルの配置場所はありません。それらの作り方も以後説明します。(それらは全てのサイトで必須ではないですが、この例では必要になります。).

+
+ +

catalogアプリケーションの登録

+ +

アプリケーションが作成されたので、ツールを実行するために(例えばデータベースにモデルを追加する)プロジェクトに登録する必要がある。 アプリケーションの登録はプロジェクトの設定でINSTALLED_APPS リストに加えることで行えます。

+ +

プロジェクトの設定ファイル locallibrary/locallibrary/settings.py を開いて、INSTALLED_APPS リストの定義を見つけましょう。そして、以下のようにリストの最後に新しい行を追加しましょう。

+ +
INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'catalog.apps.CatalogConfig', 
+]
+ +

追加した新しい行はアプリケーションの構成オブジェクト(CatalogConfig) を指定しており、それはアプリケーション作成時に/locallibrary/catalog/apps.py によって生成されています。

+ +
+

メモ: すでにたくさんの他のINSTALLED_APPS (MIDDLEWAREも同様。設定ファイルのさらに下の方にあります)が存在していることに気づいたでしょう。これらは、Django administration site をサポートすること可能にし、その結果、Djangoが使用するたくさんの機能(セッション、認証など)をサポートします。

+
+ +

データベースの指定

+ +

これは、プロジェクトで使用するデータベースを特定する場所を指しています。— 開発と本番で動作のわずかな違いを避けるために、可能な限り同じデータベースを使用するのがよいでしょう。様々なDatabases オプションを確認することができます(Django docs)。

+ +

この例では、SQLite データベースを使いましょう。なぜなら、デモンストレーションデータベースでは多くの同時アクセスを必要とせず、セットアップ作業に追加の作業が不要だからです。このデータベースがどのように設定されているかは settings.py で確認できます。 (詳細は以下にも記載しています):

+ +
DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    }
+}
+
+ +

SQLiteを使っているので、ここではこれ以上のセットアップは不要です。次へ移りましょう!

+ +

その他のプロジェクト設定

+ +

settings.py ファイルは、ほかのいくつかの設定の構成にも使用されますが、この時点では TIME_ZONE を変更するだけでよいでしょう。これは、tzデータベースタイムゾーンの標準リストの文字列を同じにする必要があります。(テーブルのTZ列に必要な値が含まれています) TIME_ZONE の値を、あなたのタイムゾーンに適した文字列に変更しましょう。
+ 例を示します:

+ +
TIME_ZONE = 'Europe/London'
+ +

今は変更しないが、次の2つの設定があることに注意してください。

+ + + +

URLマッパーの接続

+ +

Webサイトは、プロジェクトフォルダー内のURLマッパーファイル(urls.py)で作成されます。このファイルを使用してすべてのURLマッピングを管理できますが、関連付けられたアプリケーションへマッピングを延ばすのがより一般的です。

+ +

 locallibrary/locallibrary/urls.py を開いて、URLマッパーを使うためのいくつかの方法を記した説明文に注意してください。

+ +
"""locallibrary URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.0/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+]
+
+ +

The URL mappings are managed through the urlpatterns variable, which is a Python list of path() functions. Each path() function either associates a URL pattern to a specific view, which will be displayed when the pattern is matched, or with another list of URL pattern testing code (in this second case, the pattern becomes the "base URL" for patterns defined in the target module). The urlpatterns list initially defines a single function that maps all URLs with the pattern admin/ to the module admin.site.urls , which contains the Administration application's own URL mapping definitions.

+ +
+

Note: The route in path() is a string defining a URL pattern to match. This string might include a named variable (in angle brackets), e.g. 'catalog/<id>/'. This pattern will match a URL like /catalog/any_chars/ and pass any_chars to the view as a string with parameter name id). We discuss path methods and route patterns further in later topics.

+
+ +

Add the lines below to the bottom of the file in order to add a new list item to the urlpatterns list. This new item includes a path() that forwards requests with the pattern catalog/ to the module catalog.urls (the file with the relative URL /catalog/urls.py).

+ +
# Use include() to add paths from the catalog application
+from django.conf.urls import include
+from django.urls import path
+
+urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]
+
+ +

Now let's redirect the root URL of our site (i.e. 127.0.0.1:8000) to the URL 127.0.0.1:8000/catalog/; this is the only app we'll be using in this project, so we might as well. To do this, we'll use a special view function (RedirectView), which takes as its first argument the new relative URL to redirect to (/catalog/) when the URL pattern specified in the path() function is matched (the root URL, in this case).

+ +

Add the following lines, again to the bottom of the file:

+ +
#Add URL maps to redirect the base URL to our application
+from django.views.generic import RedirectView
+urlpatterns += [
+    path('', RedirectView.as_view(url='/catalog/')),
+]
+ +

Leave the first parameter of the path function empty to imply '/'. If you write the first parameter as '/' Django will give you the following warning when you start the development server:

+ +
System check identified some issues:
+
+WARNINGS:
+?: (urls.W002) Your URL pattern '/' has a route beginning with a '/'.
+Remove this slash as it is unnecessary.
+If this pattern is targeted in an include(), ensure the include() pattern has a trailing '/'.
+
+ +

Django does not serve static files like CSS, JavaScript, and images by default, but it can be useful for the development web server to do so while you're creating your site. As a final addition to this URL mapper, you can enable the serving of static files during development by appending the following lines. 

+ +

Add the following final block to the bottom of the file now:

+ +
# Use static() to add url mapping to serve static files during development (only)
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+
+ +
+

Note: There are a number of ways to extend the urlpatterns list (above we just appended a new list item using the += operator to clearly separate the old and new code). We could have instead just included this new pattern-map in the original list definition:

+ +
urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('catalog/', include('catalog.urls')),
+    path('', RedirectView.as_view(url='/catalog/', permanent=True)),
+] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+
+ +

In addition, we included the import line (from django.urls import include) with the code that uses it (so it is easy to see what we've added), but it is common to include all your import lines at the top of a Python file.

+
+ +

As a final step, create a file inside your catalog folder called urls.py, and add the following text to define the (empty) imported urlpatterns. This is where we'll add our patterns as we build the application. 

+ +
from django.urls import path
+from . import views
+
+
+urlpatterns = [
+
+]
+
+ +

ウェブサイトフレームワークのテスト

+ +

At this point we have a complete skeleton project. The website doesn't actually do anything yet, but its worth running it to make sure that none of our changes have broken anything. 

+ +

Before we do that, we should first run a database migration. This updates our database to include any models in our installed applications (and removes some build warnings).

+ +

Running database migrations

+ +

Django uses an Object-Relational-Mapper (ORM) to map Model definitions in the Django code to the data structure used by the underlying database. As we change our model definitions, Django tracks the changes and can create database migration scripts (in /locallibrary/catalog/migrations/) to automatically migrate the underlying data structure in the database to match the model.

+ +

When we created the website Django automatically added a number of models for use by the admin section of the site (which we'll look at later). Run the following commands to define tables for those models in the database (make sure you are in the directory that contains manage.py):

+ +
python3 manage.py makemigrations
+python3 manage.py migrate
+
+ +
+

Important: You'll need to run the above commands every time your models change in a way that will affect the structure of the data that needs to be stored (including both addition and removal of whole models and individual fields).

+
+ +

The makemigrations command creates (but does not apply) the migrations for all applications installed in your project (you can specify the application name as well to just run a migration for a single project). This gives you a chance to checkout the code for these migrations before they are applied — when you're a Django expert you may choose to tweak them slightly!

+ +

The migrate command actually applies the migrations to your database (Django tracks which ones have been added to the current database).

+ +
+

Note: See Migrations (Django docs) for additional information about the lesser-used migration commands.

+
+ +

Running the website

+ +

During development you can test the website by first serving it using the development web server, and then viewing it on your local web browser. 

+ +
+

Note: The development web server is not robust or performant enough for production use, but it is a very easy way to get your Django website up and running during development to give it a convenient quick test. By default it will serve the site to your local computer (http://127.0.0.1:8000/), but you can also specify other computers on your network to serve to. For more information see django-admin and manage.py: runserver (Django docs).

+
+ +

Run the development web server by calling the runserver command (in the same directory as manage.py):

+ +
python3 manage.py runserver
+
+ Performing system checks...
+
+ System check identified no issues (0 silenced).
+ September 22, 2016 - 16:11:26
+ Django version 1.10, using settings 'locallibrary.settings'
+ Starting development server at http://127.0.0.1:8000/
+ Quit the server with CTRL-BREAK.
+
+ +

Once the server is running you can view the site by navigating to http://127.0.0.1:8000/ in your local web browser. You should see a site error page that looks like this:

+ +

Django Debug page for Django 2.0

+ +

Don't worry! This error page is expected because we don't have any pages/urls defined in the catalogs.urls module (which we're redirected to when we get an URL to the root of the site). 

+ +
+

Note: The above page demonstrates a great Django feature — automated debug logging. An error screen will be displayed with useful information whenever a page cannot be found, or any error is raised by the code. In this case we can see that the URL we've supplied doesn't match any of our URL patterns (as listed). The logging will be turned off during production (when we put the site live on the Web), in which case a less informative but more user-friendly page will be served.

+
+ +

At this point we know that Django is working! 

+ +
+

Note: You should re-run migrations and re-test the site whenever you make significant changes. It doesn't take very long!

+
+ +

自分でやってみよう

+ +

The catalog/ directory contains files for the views, models, and other parts of the application. Open these files and inspect the boilerplate. 

+ +

As you saw above, a URL-mapping for the Admin site has already been added in the project's urls.py. Navigate to the admin area in your browser and see what happens (you can infer the correct URL from the mapping above).

+ + + +

要約

+ +

You have now created a complete skeleton website project, which you can go on to populate with urls, models, views, and templates.

+ +

Now the skeleton for the Local Library website is complete and running, it's time to start writing the code that makes this website do what it is supposed to do. 

+ +

参考文献

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}

+ +

このモジュール内

+ + diff --git a/files/ja/learn/server-side/django/tutorial_local_library_website/index.html b/files/ja/learn/server-side/django/tutorial_local_library_website/index.html new file mode 100644 index 0000000000..d0d0f670f2 --- /dev/null +++ b/files/ja/learn/server-side/django/tutorial_local_library_website/index.html @@ -0,0 +1,99 @@ +--- +title: 'Django チュートリアル: 地域図書館ウェブサイト' +slug: Learn/Server-side/Django/Tutorial_local_library_website +tags: + - Python + - django + - チュートリアル + - 初心者 +translation_of: Learn/Server-side/Django/Tutorial_local_library_website +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}
+ +

実践的なチュートリアルシリーズの最初の記事は、学習する内容を説明し、後の記事で進めていく「地域図書館」のサンプルウェブサイトの概要を示しています。

+ + + + + + + + + + + + +
前提条件:Djangoの紹介を読んでください。以下の記事では、Django開発環境をセットアップする必要があります。
目的:このチュートリアルで使用されているサンプルアプリケーションを紹介し、読者が何をするかを理解できるようにします。
+ +

概要

+ +

MDN "地域図書館" Djangoチュートリアルへようこそ。ここでは、地域図書館のカタログを管理するためのWebサイトを開発します。

+ +

この一連のチュートリアルの記事でやることは次の通りです:

+ + + +

これらの話題のいくつかについて学び、他の話題にも簡単に触れました。チュートリアルシリーズの最後は、あなた自身で簡単なDjangoアプリケーションを開発するのに十分な知識が必要です。

+ +

地域図書館ウェブサイト

+ +

地域図書館(LocalLibrary)は、この一連のチュートリアルの過程で作成および展開するWebサイトの名前です。ご存じのように、ウェブサイトの目的は、利用可能な書籍を閲覧してアカウントを管理できる小さな地域図書館のオンラインカタログを提供することです。

+ +

この例は慎重に選択されています。なぜなら、必要に応じて細かく表示することができ、ほぼすべてのDjango機能を表示するために使用できます。 さらに重要なことは、Django Webフレームワークの最も重要な機能をガイドする方法を提供できることです:

+ + + +

これは非常に拡張可能な例ですが、地域図書館と呼んでいます。理由は、Djangoをすぐに起動して実行するのに役立つ最小限の情報を表示したいと考えているからです。つまり、書籍、本のコピー、作者関する情報、およびその他の重要な情報は保存します。しかし、図書館が保管する可能性のある他のアイテムに関する情報を保管したり、複数の図書館サイトやその他の「大きな図書館」機能をサポートするために必要なインフラストラクチャーを提供することはありません。

+ +

詰まってます、どこでソースを入手できますか?

+ +

チュートリアルを進めるうちに、各ポイントでコピー&ペーストするための適切なコードスニペットが提供されます。また、この他に自分で拡張してほしいコードもあります(いくつかのガイダンスがあります)。

+ +

詰まった場合は、ウェブサイトの完全に開発されたバージョンをGithub上で見ることができます。

+ +

要約

+ +

地域図書館ウェブサイトについてと、何を学ぶのかをもう少し知りました。今度は例を含むスケルトンプロジェクトを作成しましょう。

+ +

{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}

+ +

 

+ +

このモジュール内

+ + + +

 

diff --git a/files/ja/learn/server-side/django/web_application_security/index.html b/files/ja/learn/server-side/django/web_application_security/index.html new file mode 100644 index 0000000000..496fac0fbd --- /dev/null +++ b/files/ja/learn/server-side/django/web_application_security/index.html @@ -0,0 +1,180 @@ +--- +title: Django Web アプリケーションのセキュリティ +slug: Learn/Server-side/Django/web_application_security +translation_of: Learn/Server-side/Django/web_application_security +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Deployment", "Learn/Server-side/Django/django_assessment_blog", "Learn/Server-side/Django")}}
+ +
ユーザーのデータを守ることはWebデザインにおいて重要です。 以前、より一般的なセキュリティの脅威の一部を Webセキュリティ の記事で説明しました— 本記事ではDjangoにビルトインされている保護機能がそのような脅威にどう対応しているか、より実践的な動きを見ながら説明していきます。
+ + + + + + + + + + + + +
前提条件:Read the サーバーサイドプログラミングの "Webサイトセキュリティ" の記事を読んでいること。Djangoチュートリアルを少なくとも Django Tutorial Part 9: Working with forms まで完了していること。
目標:Django Webアプリケーションをセキュアにするためにやるべきこと、やってはいけないことを理解する。
+ +

概要

+ +

The Website security topic provides an overview of what website security means for server-side design, and some of the more common threats that you may need to protect against. One of the key messages in that article is that almost all attacks are successful when the web application trusts data from the browser.

+ +
+

Important: The single most important lesson you can learn about website security is to never trust data from the browser. This includes GET request data in URL parameters, POST data, HTTP headers and cookies, user-uploaded files, etc. Always check and sanitize all incoming data. Always assume the worst.

+
+ +

The good news for Django users is that many of the more common threats are handled by the framework! The Security in Django (Django docs) article explains Django's security features and how to secure a Django-powered website.

+ +

Common threats/protections

+ +

Rather than duplicate the Django documentation here, in this article we'll demonstrate just a few of the security features in the context of our Django LocalLibrary tutorial.

+ +

Cross site scripting (XSS)

+ +

XSS is a term used to describe a class of attacks that allow an attacker to inject client-side scripts through the website into the browsers of other users. This is usually achieved by storing malicious scripts in the database where they can be retrieved and displayed to other users, or by getting users to click a link that will cause the attacker’s JavaScript to be executed by the user’s browser.

+ +

Django's template system protects you against the majority of XSS attacks by escaping specific characters that are "dangerous" in HTML. We can demonstrate this by attempting to inject some JavaScript into our LocalLibrary website using the Create-author form we set up in Django Tutorial Part 9: Working with forms.

+ +
    +
  1. Start the website using the development server (python3 manage.py runserver).
  2. +
  3. Open the site in your local browser and login to your superuser account.
  4. +
  5. Navigate to the author-creation page (which should be at URL: http://127.0.0.1:8000/catalog/author/create/).
  6. +
  7. Enter names and date details for a new user, and then append the following text to the Last Name field:
    + <script>alert('Test alert');</script>.
    + Author Form XSS test +
    +

    Note: This is a harmless script that, if executed, will display an alert box in your browser. If the alert is displayed when you submit the record then the site is vulnerable to XSS threats.

    +
    +
  8. +
  9. Press Submit to save the record.
  10. +
  11. When you save the author it will be displayed as shown below. Because of the XSS protections the alert() should not be run. Instead the script is displayed as plain text.Author detail view XSS test
  12. +
+ +

If you view the page HTML source code, you can see that the dangerous characters for the script tags have been turned into their harmless escape code equivalents (e.g. > is now &gt;)

+ +
<h1>Author: Boon&lt;script&gt;alert(&#39;Test alert&#39;);&lt;/script&gt;, David (Boonie) </h1>
+
+ +

Using Django templates protects you against the majority of XSS attacks. However it is possible to turn off this protection, and the protection isn't automatically applied to all tags that wouldn't normally be populated by user input (for example, the help_text in a form field is usually not user-supplied, so Django doesn't escape those values).

+ +

It is also possible for XSS attacks to originate from other untrusted source of data, such as cookies, Web services or uploaded files (whenever the data is not sufficiently sanitized before including in a page). If you're displaying data from these sources, then you may need to add your own sanitisation code.

+ +

Cross site request forgery (CSRF) protection

+ +

CSRF attacks allow a malicious user to execute actions using the credentials of another user without that user’s knowledge or consent. For example consider the case where we have a hacker who wants to create additional authors for our LocalLibrary.

+ +
+

Note: Obviously our hacker isn't in this for the money! A more ambitious hacker could use the same approach on other sites to perform much more harmful tasks (e.g. transfer money to their own accounts, etc.)

+
+ +

In order to do this, they might create an HTML file like the one below, which contains an author-creation form (like the one we used in the previous section) that is submitted as soon as the file is loaded. They would then send the file to all the Librarians and suggest that they open the file (it contains some harmless information, honest!). If the file is opened by any logged in librarian, then the form would be submitted with their credentials and a new author would be created.

+ +
<html>
+<body onload='document.EvilForm.submit()'>
+
+<form action="http://127.0.0.1:8000/catalog/author/create/" method="post" name='EvilForm'>
+  <table>
+    <tr><th><label for="id_first_name">First name:</label></th><td><input id="id_first_name" maxlength="100" name="first_name" type="text" value="Mad" required /></td></tr>
+    <tr><th><label for="id_last_name">Last name:</label></th><td><input id="id_last_name" maxlength="100" name="last_name" type="text" value="Man" required /></td></tr>
+    <tr><th><label for="id_date_of_birth">Date of birth:</label></th><td><input id="id_date_of_birth" name="date_of_birth" type="text" /></td></tr>
+    <tr><th><label for="id_date_of_death">Died:</label></th><td><input id="id_date_of_death" name="date_of_death" type="text" value="12/10/2016" /></td></tr>
+  </table>
+  <input type="submit" value="Submit" />
+</form>
+
+</body>
+</html>
+
+ +

Run the development web server, and log in with your superuser account. Copy the text above into a file and then open it in the browser. You should get a CSRF error, because Django has protection against this kind of thing!

+ +

The way the protection is enabled is that you include the {% csrf_token %} template tag in your form definition. This token is then rendered in your HTML as shown below, with a value that is specific to the user on the current browser.

+ +
<input type='hidden' name='csrfmiddlewaretoken' value='0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW' />
+
+ +

Django generates a user/browser specific key and will reject forms that do not contain the field, or that contain an incorrect field value for the user/browser.

+ +

To use this type of attack the hacker now has to discover and include the CSRF key for the specific target user. They also can't use the "scattergun" approach of sending a malicious file to all librarians and hoping that one of them will open it, since the CSRF key is browser specific.

+ +

Django's CSRF protection is turned on by default. You should always use the {% csrf_token %} template tag in your forms and use POST for requests that might change or add data to the database.

+ +

Other protections

+ +

Django also provides other forms of protection (most of which would be hard or not particularly useful to demonstrate):

+ +
+
SQL injection protection
+
SQL injection vulnerabilities enable malicious users to execute arbitrary SQL code on a database, allowing data to be accessed, modified, or deleted irrespective of the user's permissions. In almost every case you'll be accessing the database using Django’s querysets/models, so the resulting SQL will be properly escaped by the underlying database driver. If you do need to write raw queries or custom SQL then you'll need to explicitly think about preventing SQL injection.
+
Clickjacking protection
+
In this attack a malicious user hijacks clicks meant for a visible top level site and routes them to a hidden page beneath. This technique might be used, for example, to display a legitimate bank site but capture the login credentials in an invisible <iframe> controlled by the attacker. Django contains clickjacking protection in the form of the X-Frame-Options middleware which, in a supporting browser, can prevent a site from being rendered inside a frame.
+
Enforcing SSL/HTTPS
+
SSL/HTTPS can be enabled on the web server in order to encrypt all traffic between the site and browser, including authentication credentials that would otherwise be sent in plain text (enabling HTTPS is highly recommended). If HTTPS is enabled then Django provides a number of other protections you can use:
+
+ + + +
+
Host header validation
+
Use ALLOWED_HOSTS to only accept requests from trusted hosts.
+
+ +

There are many other protections, and caveats to the usage of the above mechanisms. While we hope that this has given you an overview of what Django offers, you should still read the Django security documentation.

+ + + +

Summary

+ +

Django has effective protections against a number of common threats, including XSS and CSRF attacks. In this article we've demonstrated how those particular threats are handled by Django in our LocalLibrary website. We've also provided a brief overview of some of the other protections.

+ +

This has been a very brief foray into web security. We strongly recommend that you read Security in Django to gain a deeper understanding.

+ +

The next and final step in this module about Django is to complete the assessment task.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Deployment", "Learn/Server-side/Django/django_assessment_blog", "Learn/Server-side/Django")}}

+ +

 

+ +

In this module

+ + + +

 

diff --git a/files/ja/learn/server-side/express_nodejs/deployment/index.html b/files/ja/learn/server-side/express_nodejs/deployment/index.html new file mode 100644 index 0000000000..6f8b60f094 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/deployment/index.html @@ -0,0 +1,525 @@ +--- +title: 'Express チュートリアル Part 7: プロダクションへのデプロイ' +slug: Learn/Server-side/Express_Nodejs/deployment +tags: + - CodingScripting + - Express + - Node + - heroku + - サーバサイド + - デプロイ + - 初心者 + - 学習 +translation_of: Learn/Server-side/Express_Nodejs/deployment +--- +
{{LearnSidebar}}
+ +
{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}
+ +

これで素晴らしい地域図書館 Web サイトを作成 (およびテスト) したので、公共の Web サーバーにインストールして、図書館のスタッフとメンバーがインターネット経由でアクセスできるようにします。この記事では Web サイトをデプロイするためのホストを見つける方法、およびサイトを運用に向けて準備するために必要な作業の概要について説明します。

+ + + + + + + + + + + + +
前提条件:Express チュートリアル Part 6: フォームの操作を含む、これまでのチュートリアルのトピックをすべて完了してください。
目標:Express アプリケーションをプロダクションにデプロイする場所と方法を学ぶ。
+ +

Overview

+ +

Once your site is finished (or finished "enough" to start public testing) you're going to need to host it somewhere more public and accessible than your personal development computer.

+ +

Up to now, you've been working in a development environment, using Express/Node as a web server to share your site to the local browser/network, and running your website with (insecure) development settings that expose debugging and other private information. Before you can host a website externally you're first going to have to:

+ + + +

This tutorial provides some guidance on your options for choosing a hosting site, a brief overview of what you need to do in order to get your Express app ready for production, and a worked example of how to install the LocalLibrary website onto the Heroku cloud hosting service.

+ +

Bear in mind that you don't have to use Heroku — there are other hosting services available. We've also provided a separate tutorial to show how to Install LocalLibrary on PWS/Cloud Foundry.

+ +

What is a production environment?

+ +

The production environment is the environment provided by the server computer where you will run your website for external consumption. The environment includes:

+ + + +

The server computer could be located on your premises and connected to the Internet by a fast link, but it is far more common to use a computer that is hosted "in the cloud". What this actually means is that your code is run on some remote computer (or possibly a "virtual" computer) in your hosting company's data center(s). The remote server will usually offer some guaranteed level of computing resources (e.g. CPU, RAM, storage memory, etc.) and Internet connectivity for a certain price.

+ +

This sort of remotely accessible computing/networking hardware is referred to as Infrastructure as a Service (IaaS). Many IaaS vendors provide options to preinstall a particular operating system, onto which you must install the other components of your production environment. Other vendors allow you to select more fully-featured environments, perhaps including a complete Node setup.

+ +
+

Note: Pre-built environments can make setting up your website very easy because they reduce the configuration, but the available options may limit you to an unfamiliar server (or other components) and may be based on an older version of the OS. Often it is better to install components yourself so that you get the ones that you want, and when you need to upgrade parts of the system, you have some idea of where to start!

+
+ +

Other hosting providers support Express as part of a Platform as a Service (PaaS) offering. When using this sort of hosting you don't need to worry about most of your production environment (servers, load balancers, etc.) as the host platform takes care of those for you. That makes deployment quite easy because you just need to concentrate on your web application and not any other server infrastructure.

+ +

Some developers will choose the increased flexibility provided by IaaS over PaaS, while others will appreciate the reduced maintenance overhead and easier scaling of PaaS. When you're getting started, setting up your website on a PaaS system is much easier, so that is what we'll do in this tutorial.

+ +
+

Tip: If you choose a Node/Express-friendly hosting provider they should provide instructions on how to set up an Express website using different configurations of web server, application server, reverse proxy, etc. For example, there are many step-by-step guides for various configurations in the Digital Ocean Node community docs.

+
+ +

Choosing a hosting provider

+ +

There are numerous hosting providers that are known to either actively support or work well with Node (and Express). These vendors provide different types of environments (IaaS, PaaS), and different levels of computing and network resources at different prices.

+ +
+

Tip: There are a lot of hosting solutions, and their services and pricing can change over time. While we introduce a few options below, it is worth performing your own Internet search before selecting a hosting provider.

+
+ +

Some of the things to consider when choosing a host:

+ + + +

The good news when you're starting out is that there are quite a few sites that provide computing environments for "free", albeit with some conditions. For example, Heroku provides a free but resource-limited PaaS environment "forever", while Amazon Web Services, Microsoft Azure, and the open source option PWS/Cloud Foundry provide free credit when you first join.

+ +

Many providers also have a "basic" tier that provides more useful levels of computing power and fewer limitations. Digital Ocean is an example of a popular hosting provider that offers a relatively inexpensive basic computing tier (in the $5 per month lower range at time of writing).

+ +
+

Note: Remember that price is not the only selection criterion. If your website is successful, it may turn out that scalability is the most important consideration.

+
+ +

Getting your website ready to publish

+ +

The main things to think about when publishing your website are web security and performance. At the bare minimum, you will want to remove the stack traces that are included on error pages during development, tidy up your logging, and set the appropriate headers to avoid many common security threats.

+ +

In the following subsections, we outline the most important changes that you should make to your app.

+ +
+

Tip: There are other useful tips in the Express docs — see Production best practices: performance and reliability and Production Best Practices: Security.

+
+ +

Set NODE_ENV to 'production'

+ +

We can remove stack traces in error pages by setting the NODE_ENV environment variable to production (it is set to 'development' by default). In addition to generating less-verbose error messages, setting the variable to production caches view templates and CSS files generated from CSS extensions. Tests indicate that setting NODE_ENV to production can improve app performance by a factor of three!

+ +

This change can be made either by using export or an environment file or using the OS initialization system.  

+ +
+

Note: This is actually a change you make in your environment setup rather than your app, but important enough to note here! We'll show how this is set for our hosting example below. 

+
+ +

Log appropriately

+ +

Logging calls can have an impact on a high-traffic website. In a production environment, you may need to log website activity (e.g. tracking traffic or logging API calls) but you should attempt to minimize the amount of logging added for debugging purposes.

+ +

One way to minimize "debug" logging in production is to use a module like debug that allows you to control what logging is performed by setting an environment variable. For example, the code fragment below shows how you might set up "author" logging. The debug variable is declared with the name 'author', and the prefix "author" will be automatically displayed for all logs from this object.

+ +
var debug = require('debug')('author');
+
+// Display Author update form on GET
+exports.author_update_get = function(req, res, next) {
+
+    req.sanitize('id').escape().trim();
+    Author.findById(req.params.id, function(err, author) {
+        if (err) {
+            debug('update error:' + err);
+            return next(err);
+        }
+        //On success
+        res.render('author_form', { title: 'Update Author', author: author });
+    });
+
+};
+ +

You can then enable a particular set of logs by specifying them as a comma-separated list in the DEBUG environment variable. You can set the variables for displaying author and book logs as shown (wildcards are also supported).

+ +
#Windows
+set DEBUG=author,book
+
+#Linux
+export DEBUG="author,book"
+
+ +
+

Challenge: Calls to debug can replace logging you might previously have done using console.log() or console.error(). Replace any console.log() calls in your code with logging via the debug module. Turn the logging on and off in your development environment by setting the DEBUG variable and observe the impact this has on logging.

+
+ +

If you need to log website activity you can use a logging library like Winston or Bunyan. For more information on this topic see: Production best practices: performance and reliability.

+ +

Use gzip/deflate compression for responses

+ +

Web servers can often compress the HTTP response sent back to a client, significantly reducing the time taken to for the client to get and load the page. The compression method used will depend on what decompression methods the client says that it supports in the request (if no compression methods are supported the response will be sent uncompressed).

+ +

You can add this to your site using compression middleware. Install this to your project by running the following command at the root of the project.

+ +
npm install compression
+ +

Open ./app.js and require the compression library as shown. Add the compression library to the middleware chain with the use() method (this should appear before any routes that you want compressed — in this case, all of them!)

+ +
var catalogRouter = require('./routes/catalog'); //Import routes for "catalog" area of site
+var compression = require('compression');
+
+// Create the Express application object
+var app = express();
+
+...
+
+app.use(compression()); //Compress all routes
+
+app.use(express.static(path.join(__dirname, 'public')));
+
+app.use('/', indexRouter);
+app.use('/users', usersRouter);
+app.use('/catalog', catalogRouter);  // Add catalog routes to middleware chain.
+
+...
+
+ +
+

Note: For a high-traffic website in production you wouldn't use this middleware. Instead, you would use a reverse proxy like Nginx.

+
+ +

Use Helmet to protect against well known vulnerabilities

+ +

Helmet is a middleware package that can help protect your app from some well-known web vulnerabilities by setting appropriate HTTP headers (see the docs for more information on what headers it sets/vulnerabilities it protects against). 

+ +

Install this to your project by running the following command at the root of the project.

+ +
npm install helmet
+
+ +

Open ./app.js and require the helmet library as shown. Then add the module to the middleware chain with the use() method.

+ +
var compression = require('compression');
+var helmet = require('helmet');
+
+// Create the Express application object
+var app = express();
+
+app.use(helmet());
+...
+ +
+

Note: The command above adds the subset of available headers that makes sense for most sites. You can add/disable specific headers as needed by following the instructions on npm.

+
+ +

Example: Installing LocalLibrary on Heroku

+ +

This section provides a practical demonstration of how to install LocalLibrary on the Heroku PaaS cloud.

+ +

Why Heroku?

+ +

Heroku is one of the longest-running and popular cloud-based PaaS services. It originally supported only Ruby apps, but now can be used to host apps from many programming environments, including Node (and hence Express)!

+ +

We are choosing to use Heroku for several reasons:

+ + + +

While Heroku is perfect for hosting this demonstration it may not be perfect for your real website. Heroku makes things easy to set up and scale, at the cost of being less flexible, and potentially a lot more expensive once you get out of the free tier.

+ +

How does Heroku work?

+ +

Heroku runs websites within one or more "Dynos", which are isolated, virtualized Unix containers that provide the environment required to run an application. The dynos are completely isolated and have an ephemeral file system (a short-lived file system that is cleaned/emptied every time the dyno restarts). The only thing that dynos share by default are application configuration variables. Heroku internally uses a load balancer to distribute web traffic to all "web" dynos. Since nothing is shared between them, Heroku can scale an app horizontally by adding more dynos (though of course, you may also need to scale your database to accept additional connections).

+ +

Because the file system is ephemeral you can't install the services required by your application directly (e.g. databases, queues, caching systems, storage, email services, etc). Instead, Heroku web applications use backing services provided as independent "add-ons" by Heroku or 3rd parties. Once attached to your web application, the add-on services are accessed in your web application via environment variables.

+ +

In order to execute your application Heroku needs to be able to set up the appropriate environment and dependencies, and also understand how it is launched. For Node apps, all the information it needs is obtained from your package.json file.

+ +

Developers interact with Heroku using a special client app/terminal, which is much like a Unix bash script. This allows you to upload code that is stored in a git repository, inspect the running processes, see logs, set configuration variables, and much more!

+ +

In order to get our application to work on Heroku, we'll need to put our Express web application into a git repository and make some minor changes to the package.json. Once we've done that we can set up a Heroku account, get the Heroku client, and use it to install our website.

+ +

That's all the overview you need in order to get started (see Getting Started on Heroku with Node.js for a more comprehensive guide).

+ +

Creating an application repository in Github

+ +

Heroku is closely integrated with the git source code version control system, using it to upload/synchronize any changes you make to the live system. It does this by adding a new Heroku "remote" repository named heroku pointing to a repository for your source on the Heroku cloud. During development, you use git to store changes on your "master" repository. When you want to deploy your site, you sync your changes to the Heroku repository.

+ +
+

Note: If you're used to following good software development practices you are probably already using git or some other SCM system. If you already have a git repository, then you can skip this step.

+
+ +

There are a lot of ways of to work with git, but one of the easiest is to first set up an account on GitHub, create the repository there, and then sync to it locally:

+ +
    +
  1. Visit https://github.com/ and create an account.
  2. +
  3. Once you are logged in, click the + link in the top toolbar and select New repository.
  4. +
  5. Fill in all the fields on this form. While these are not compulsory, they are strongly recommended. +
      +
    • Enter a new repository name (e.g. express-locallibrary-tutorial), and description (e.g. "Local Library website written in Express (Node)".
    • +
    • Choose Node in the Add .gitignore selection list.
    • +
    • Choose your preferred license in the Add license selection list.
    • +
    • Check Initialize this repository with a README.
    • +
    +
  6. +
  7. Press Create repository.
  8. +
  9. Click the green "Clone or download" button on your new repo page.
  10. +
  11. Copy the URL value from the text field inside the dialog box that appears (it should be something like: https://github.com/<your_git_user_id>/express-locallibrary-tutorial.git).
  12. +
+ +

Now the repository ("repo") is created we are going to want to clone it on our local computer:

+ +
    +
  1. Install git for your local computer (you can find versions for different platforms here).
  2. +
  3. Open a command prompt/terminal and clone your repository using the URL you copied above: +
    git clone https://github.com/<your_git_user_id>/express-locallibrary-tutorial.git
    +
    + This will create the repository below the current point.
  4. +
  5. Navigate into the new repo. +
    cd express-locallibrary-tutorial
    +
  6. +
+ +

The final step is to copy in your application and then add the files to your repo using git:

+ +
    +
  1. Copy your Express application into this folder (excluding /node_modules, which contains dependency files that you should fetch from NPM as needed).
  2. +
  3. Open a command prompt/terminal and use the add command to add all files to git.
  4. +
  5. +
    git add -A
    +
    +
  6. +
  7. Use the status command to check all files that you are about to add are correct (you want to include source files, not binaries, temporary files etc.). It should look a bit like the listing below. +
    > git status
    +On branch master
    +Your branch is up-to-date with 'origin/master'.
    +Changes to be committed:
    +  (use "git reset HEAD <file>..." to unstage)
    +
    +        new file:   ...
    +
  8. +
  9. When you're satisfied commit the files to your local repository: +
    git commit -m "First version of application moved into github"
    +
  10. +
  11. Then synchronize your local repository to the Github website, using the following: +
    git push origin master
    +
  12. +
+ +

When this operation completes, you should be able to go back to the page on Github where you created your repo, refresh the page, and see that your whole application has now been uploaded. You can continue to update your repository as files change using this add/commit/push cycle.

+ +
+

Tip: This is a good point to make a backup of your "vanilla" project — while some of the changes we're going to be making in the following sections might be useful for deployment on any platform (or development) others might not.

+ +

The best way to do this is to use git to manage your revisions. With git you can not only go back to a particularly old version, but you can maintain this in a separate "branch" from your production changes and cherry-pick any changes to move between production and development branches. Learning Git is well worth the effort, but is beyond the scope of this topic.

+ +

The easiest way to do this is to just copy your files into another location. Use whichever approach best matches your knowledge of git!

+
+ +

Update the app for Heroku

+ +

This section explains the changes you'll need to make to our LocalLibrary application to get it to work on Heroku.

+ +

Set node version 

+ +

The package.json contains everything needed to work out your application dependencies and what file should be launched to start your site. Heroku detects the presence of this file, and will use it to provision your app environment.

+ +

The only useful information missing in our current package.json is the version of node. We can find the version of node we're using for development by entering the command:

+ +
>node --version
+v8.9.1
+ +

Open package.json, and add this information as an engines > node section as shown (using the version number for your system).

+ +
{
+  "name": "express-locallibrary-tutorial",
+  "version": "0.0.0",
+  "engines": {
+    "node": "8.9.1"
+  },
+  "private": true,
+  ...
+
+ +

Database configuration

+ +

So far in this tutorial, we've used a single database that is hard-coded into app.js. Normally we'd like to be able to have a different database for production and development, so next we'll modify the LocalLibrary website to get the database URI from the OS environment (if it has been defined), and otherwise use our development database.

+ +

Open app.js and find the line that sets the MongoDB connection variable. It will look something like this:

+ +
var mongoDB = 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library';
+ +

Replace the line with the following code that uses process.env.MONGODB_URI to get the connection string from an environment variable named MONGODB_URI if has been set (use your own database URL instead of the placeholder below.)

+ +
var mongoDB = process.env.MONGODB_URI || 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library';
+
+ +

Get dependencies and re-test

+ +

Before we proceed, let's test the site again and make sure it wasn't affected by any of our changes. 

+ +

First, we will need to fetch our dependencies (you will recall we didn't copy the node_modules folder into our git tree). You can do this by running the following command in your terminal at the root of the project:

+ +
npm install
+
+ +

Now run the site (see Testing the routes for the relevant commands) and check that the site still behaves as you expect.

+ +

Save changes to Github

+ +

Next, let's save all our changes to Github. In the terminal (whilst inside our repository), enter the following commands:

+ +
git add -A
+git commit -m "Added files and changes required for deployment to heroku"
+git push origin master
+ +

We should now be ready to start deploying LocalLibrary on Heroku.

+ +

Get a Heroku account

+ +

To start using Heroku you will first need to create an account (skip ahead to Create and upload the website if you've already got an account and installed the Heroku client):

+ + + +

Install the client

+ +

Download and install the Heroku client by following the instructions on Heroku here.

+ +

After the client is installed you will be able to run commands. For example to get help on the client:

+ +
heroku help
+
+ +

Create and upload the website

+ +

To create the app we run the "create" command in the root directory of our repository. This creates a git remote ("pointer to a remote repository") named heroku in our local git environment.

+ +
heroku create
+ +
+

Note: You can name the remote if you like by specifying a value after "create". If you don't then you'll get a random name. The name is used in the default URL.

+
+ +

We can then push our app to the Heroku repository as shown below. This will upload the app, get all its dependencies, package it in a dyno, and start the site.

+ +
git push heroku master
+ +

If we're lucky, the app is now "running" on the site. To open your browser and run the new website, use the command:

+ +
heroku open
+ +
+

Note: The site will be running using our development database. Create some books and other objects, and check out whether the site is behaving as you expect. In the next section, we'll set it to use our new database.

+
+ +

Setting configuration variables

+ +

You will recall from a preceding section that we need to set NODE_ENV to 'production' in order to improve our performance and generate less-verbose error messages. We do this by entering the following command:

+ +
>heroku config:set NODE_ENV='production'
+Setting NODE_ENV and restarting limitless-tor-18923... done, v13
+NODE_ENV: production
+
+ +

We should also use a separate database for production, setting its URI in the MONGODB_URI  environment variable. You can set up a new database and database-user exactly as we did originally, and get its URI. You can set the URI as shown (obviously, using your own URI!)

+ +
>heroku config:set MONGODB_URI='mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production'
+Setting MONGODB_URI and restarting limitless-tor-18923... done, v13
+MONGODB_URI: mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production
+
+ +

You can inspect your configuration variables at any time using the heroku config command — try this now:

+ +
>heroku config
+=== limitless-tor-18923 Config Vars
+MONGODB_URI: mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production
+NODE_ENV:    production
+
+ +

Heroku will restart your app when it updates the variables. If you check the home page now it should show zero values for your object counts, as the changes above mean that we're now using a new (empty) database.

+ +

Managing addons

+ +

Heroku uses independent add-ons to provide backing services to apps — for example, email or database services. We don't use any addons in this website, but they are an important part of working with Heroku, so you may want to check out the topic Managing Add-ons (Heroku docs).

+ +

Debugging

+ +

The Heroku client provides a few tools for debugging:

+ +
heroku logs  # Show current logs
+heroku logs --tail # Show current logs and keep updating with any new results
+heroku ps   #Display dyno status
+
+ + + +

まとめ

+ +

これで、本番環境での Express アプリケーションの設定に関するこのチュートリアル、および Express を使用した作業に関する一連のチュートリアルは終了です。それらが役に立つことを願っています。完全に完成したバージョンの Github のソースコードをここでチェックすることができます。

+ +

あわせて参照

+ + + +

{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/development_environment/index.html b/files/ja/learn/server-side/express_nodejs/development_environment/index.html new file mode 100644 index 0000000000..7bfa26bb88 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/development_environment/index.html @@ -0,0 +1,410 @@ +--- +title: Node 開発環境の設定 +slug: Learn/Server-side/Express_Nodejs/development_environment +tags: + - CodingScripting + - Express + - Intro + - Learn + - Node + - nodejs + - npm + - server-side + - イントロダクション + - サーバサイド + - 初心者 + - 学習 + - 開発環境 +translation_of: Learn/Server-side/Express_Nodejs/development_environment +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}
+ +

Express の目的が理解できたので、Windows、Linux (Ubuntu)、および macOS 上で Node/Express 開発環境をセットアップしてテストする方法を説明します。どのような一般的な OS を使用していても、この記事では Express アプリケーションの開発を開始するために必要なものを提供します。

+ + + + + + + + + + + + +
前提条件:端末/コマンドラインを開く方法を知っている。開発用コンピューターの OS にソフトウェアパッケージをインストールする方法を知っている。
目標:コンピューター上に Express (X.XX) 用の開発環境をセットアップします。
+ +

Express 開発環境概要

+ +

Node と Express のおかげでウェブアプリケーションの開発を始めるためにコンピューターをセットアップすることが非常に簡単になります。このセクションでは必要なツールの概要、Ubuntu、macOS、および Windows に Node (および Express) をインストールするための最も簡単な方法について説明し、インストールをテストする方法を示します。

+ +

Express 開発環境とは何か?

+ +

Express 開発環境には NodejsNPM パッケージマネージャー、および (オプションで) ローカルコンピューターに Express Application Generator がインストールされています。

+ +

Node と NPM パッケージマネージャーは、準備されたバイナリパッケージ、インストーラー、オペレーティングシステムのパッケージマネージャー、またはソースから一緒にインストールされます (次のセクションを参照)。 Express は、NPM によって、個々の Express ウェブアプリケーションの依存関係として (テンプレートエンジン、データベースドライバー、認証ミドルウェア、静的ファイルを提供するためのミドルウェアなどの他のライブラリと共に) インストールされます。

+ +

NPMMVC パターンに従ったスケルトンの Express ウェブアプリケーションを作成するための便利なツールである Express Application Generatorを (グローバルに) インストールするためにも使用できます。Express を使用するアプリを作成したり、同じアーキテクチャ上のレイアウトや依存関係を持つ Express アプリを構築したりするためにこのツールを使用する必要はないため、アプリケーションジェネレーターはオプションです。ただし、使い始めるのがはるかに簡単になり、モジュール式のアプリケーション構造が促進されるため、これを使用します。

+ +
+

メモ: 他のウェブフレームワークとは異なり、開発環境には独立した開発用の ウェブサーバーは含まれていません。Node/Express では、ウェブアプリケーションが独自のウェブサーバーを作成して実行します。

+
+ +

テキストエディタやコード編集用の IDE、コードの異なるバージョンを安全に管理するための Git などのソース管理マネジメントツールなど、一般的な開発環境の一部である他の周辺ツールもあります。これらの種類のツール (特にテキストエディタ) が既にインストールされていると仮定しています。

+ +

どのオペレーティングシステムがサポートされていますか?

+ +

Node は Windows、macOS、Linux の多くの「フレーバー」、Docker などで実行できます (nodejs のダウンロードページに完全なリストがあります)。ほとんどのパーソナルコンピューターは開発中に Node を実行するのに必要な性能を持っているはずです。ExpressNode 環境で実行されるため、Node を実行する任意のプラットフォームで実行できます。

+ +

この記事では Windows、macOS、および Ubuntu Linux のセットアップ手順を説明します。

+ +

どのバージョンの Node/Express を使用すべきですか?

+ +

たくさんの Node のリリースがあります - 新しいリリースにはバグ修正、ECMAScript (JavaScript) 標準のより最新のバージョンのサポート、そして Node API の改良が含まれています。

+ +

一般的には最新の LTS (長期サポート) リリースを使用するべきです。比較的最新の機能を持ちながら (そして現在も積極的にメンテナンスされています)、"最新の" リリースより安定しているからです。LTS バージョンに存在しない機能が必要な場合は、最新版リリースを使用してください。

+ +

Express は常に最新のバージョンを使うべきです。

+ +

データベースやその他の依存関係についてはどうですか?

+ +

データベースドライバー、テンプレートエンジン、認証エンジンなどのその他の依存関係はアプリケーションの一部であり、NPM パッケージマネージャーを使用してアプリケーション環境にインポートされます。それらについては、後のアプリ固有の記事で説明します。

+ +

Node のインストール

+ +

Express を使用するには、まず Nodejs Node Package Manager (NPM) をオペレーティングシステムにインストールする必要があります。以下のセクションでは Ubuntu Linux 18.04、macOS、および Windows 10 に Long Term Supported (LTS) バージョンの Nodejs をインストールする最も簡単な方法について説明します。

+ +
+

Tip: 以下のセクションは、ターゲット OS プラットフォームに Node NPM をインストールする最も簡単な方法を示しています。他の OS を使用している場合、または現在のプラットフォームで他の方法を使用したい場合は、パッケージマネージャーによる Node.js のインストール (nodejs.org) を参照してください。

+
+ +

macOS および Windows

+ +

Windows と macOS への Node NPM のインストールは、提供されているインストーラーを使用することができるため、簡単です。

+ +
    +
  1. 必要なインストーラーをダウンロードします +
      +
    1. https://nodejs.org/ja/ に進みます
    2. +
    3. "ほとんどのユーザーに推奨" である LTS ビルドをダウンロードするためのボタンを選択してください。
    4. +
    +
  2. +
  3. ダウンロードしたファイルをダブルクリックし、インストールの指示に従って Node をインストールします。
  4. +
+ +

Ubuntu 18.04

+ +

Node 10.x の最新の LTS バージョンをインストールする最も簡単な方法は、パッケージマネージャーを使ってそれを Ubuntu バイナリ配布リポジトリーから入手することです。これはあなたの端末で以下の2つのコマンドを実行することによって非常に簡単に行うことができます。

+ +
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
+sudo apt-get install -y nodejs
+
+ +
+

警告: それらは非常に古いバージョンの Node を含んでいるので、通常の Ubuntu リポジトリーから直接インストールしないでください。

+
+ +
    +
+ +

Nodejs および NPM インストールのテスト

+ +

Node がインストールされていることをテストする最も簡単な方法は、ターミナル/コマンドプロンプトで "version" コマンドを実行し、バージョン文字列が返されることを確認することです。

+ +
>node -v
+v10.16.0
+ +

Nodejs パッケージマネージャー NPM もインストールされているはずで、同じ方法でテストできます。

+ +
>npm -v
+6.9.0
+ +

もう少し刺激的なテストとして、ブラウザーで正しい URL にアクセスしたときにブラウザーに「Hello World」を単純に出力する、非常に基本的な "純粋な Node" サーバーを作成しましょう。

+ +
    +
  1. 次のテキストを hellonode.js というファイルにコピーします。これは純粋な Node 関数 (Express からは何もしていません) といくつかの ES6 構文を使用します。 + +
    //HTTP モジュールを読み込む
    +const http = require("http");
    +const hostname = '127.0.0.1';
    +const port = 3000;
    +
    +//HTTP サーバーを作成し、3000 番ポートでリクエストを待機します。
    +const server = http.createServer((req, res) => {
    +
    +  //HTTP ステータスとコンテンツタイプを持つ応答 HTTP ヘッダーを設定します。
    +  res.statusCode = 200;
    +  res.setHeader('Content-Type', 'text/plain');
    +  res.end('Hello World\n');
    +});
    +
    +//3000 番ポートでリクエストを待機し、受信したときにログ出力するコールバック関数
    +server.listen(port, hostname, () => {
    +  console.log(`Server running at http://${hostname}:${port}/`);
    +});
    +
    +
    + +

    このコードは "http" モジュールをインポートし、それを使用して 3000 番ポートで HTTP リクエストを待機するサーバーを作成 (createServer()) します。次に、スクリプトはサーバーをテストするために使用できるブラウザー URL についてのメッセージをコンソールに出力します。 createServer() 関数は、HTTP リクエストを受信したときに呼び出されるコールバック関数を引数として取ります。これは HTTP ステータスコード 200 ("OK") とプレーンテキスト "Hello World" のレスポンスを返します。

    + +
    +

    メモ:  このコードが何をしているのか正確に理解できなくても心配しないでください。Express を使い始めたら、コードについて詳しく説明します。

    +
    +
  2. +
  3. コマンドプロンプトで hellonode.js ファイルと同じディレクトリに移動し、次のようにスクリプト名とともに node を呼び出してサーバーを起動します。 +
    >node hellonode.js
    +Server running at http://127.0.0.1:3000/
    +
    +
  4. +
  5. http://127.0.0.1:3000 の URL に移動します。すべてがうまくいったら、ブラウザーは単に文字列 "Hello World" を表示するはずです。
  6. +
+ +

NPM の使用

+ +

Node 自体の次に、NPM は Node アプリケーションを操作するための最も重要なツールです。NPM は、アプリケーションが開発、テスト、および/または運用に必要なパッケージ(JavaScript ライブラリ) を取得するために使用されます。また、開発プロセスで使用されるテストやツールを実行するために使用されることもあります。

+ +
+

メモ: Node の観点からすると、Express は NPM を使用してインストールしてから独自のコードで必要とするもう1つのパッケージです。

+
+ +

手動で NPM を使用して、必要な各パッケージを別々に取り出すことができます。通常、代わりに package.json というプレーンテキストの定義ファイルを使用して依存関係を管理します。このファイルにはパッケージの名前、バージョン、説明、実行する初期ファイル、プロダクション依存関係、開発依存関係、それが動作可能な Node のバージョンなど、特定のJavaScript "package" に対するすべての依存関係が一覧表示されます。package.json ファイルには、NPM がアプリケーションを取得して実行するために必要なものがすべて含まれている必要があります (再利用可能なライブラリを作成している場合は、この定義を使用してパッケージを npm リポジトリーにアップロードし、他のユーザが利用できるようにします)。

+ +

依存関係の追加

+ +

次の手順では NPM を使用してパッケージをダウンロードし、それをプロジェクトの依存関係に保存してから、それを Node アプリケーションで要求する方法を示します。

+ +
+

メモ: ここでは Express パッケージを取得してインストールするための手順を示します。後で、このパッケージなどが Express Application Generator を使用してすでにどのように指定されているかを示します。このセクションは NPM がどのように機能するのか、および Application Generator によって何が作成されているのかを理解するのに役立ちます。

+
+ +
    +
  1. +

    まず、新しいアプリケーション用のディレクトリーを作成し、そこに移動します。

    + +
    mkdir myapp
    +cd myapp
    +
  2. +
  3. +

    アプリケーション用の package.json ファイルを作成するには、npm init コマンドを使用します。このコマンドはアプリケーションの名前とバージョン、初期エントリポイントファイルの名前 (デフォルトでは index.js) など、さまざまなことを要求します。今のところ、デフォルトをそのまま使用します。

    + +
    npm init
    + +

    package.json ファイル (cat package.json) を表示すると、受け入れたデフォルトが表示され、最後にライセンスが表示されます。

    + +
    {
    +  "name": "myapp",
    +  "version": "1.0.0",
    +  "description": "",
    +  "main": "index.js",
    +  "scripts": {
    +    "test": "echo \"Error: no test specified\" && exit 1"
    +  },
    +  "author": "",
    +  "license": "ISC"
    +}
    +
    +
  4. +
  5. +

    myapp ディレクトリーに Express をインストールし、それをあなたの package.json ファイルの依存関係リストに保存してください。

    + +
      npm install express --save
    +
    +
  6. +
  7. +

    package.json の依存関係セクションが package.json ファイルの最後に表示され、Express が含まれます。

    + +
    {
    +  "name": "myapp",
    +  "version": "1.0.0",
    +  "description": "",
    +  "main": "index.js",
    +  "scripts": {
    +    "test": "echo \"Error: no test specified\" && exit 1"
    +  },
    +  "author": "",
    +  "license": "ISC",
    +  "dependencies": {
    +    "express": "^4.16.3"
    +  }
    +}
    +
    +
  8. +
  9. このライブラリを使用するには、index.js ファイルで以下に示すように require() 関数を呼び出します。"myapp" アプリケーションディレクトリーのルートにファイルを作り、以下の内容を記述します。 +
    const express = require('express')
    +const app = express();
    +
    +app.get('/', (req, res) => {
    +  res.send('Hello World!')
    +});
    +
    +app.listen(8000, () => {
    +  console.log('Example app listening on port 8000!')
    +});
    +
    + +

    このコードは、最小限の「HelloWorld」Express ウェブアプリケーションを示しています。これは「express」モジュールをインポートし、それを使用して 8000 番ポートで HTTP リクエストを待機するサーバ ー(app) を作成し、サーバーをテストするために使用できるブラウザー URL を説明するメッセージをコンソールに出力します。 app.get() 関数は、指定された URLパス ('/') で HTTP GET リクエストにのみ応答します。この場合、関数を呼び出して Hello World! メッセージを送信します。

    +
  10. +
  11. コマンドプロンプトでスクリプトを使用して node を呼び出すことでサーバーを起動できます。 +
    >node index.js
    +Example app listening on port 8000
    +
    +
  12. +
  13. URL (http://127.0.0.1:8000/) に移動します。すべてがうまくいったら、ブラウザーは単に文字列 "Hello World!" を表示するはずです。
  14. +
+ +

開発の依存関係

+ +

依存関係が開発中にのみ使用される場合は、代わりに "開発依存関係" として保存する必要があります (パッケージユーザーが本番環境にインストールする必要がないようにするため)。たとえば、一般的な JavaScript Linting ツールの eslint を使用するには、次のように NPM を呼び出します。

+ +
npm install eslint --save-dev
+ +

次のエントリがアプリケーションの package.json に追加されます。

+ +
  "devDependencies": {
+    "eslint": "^4.12.1"
+  }
+
+ +
+

メモ: "Lint" は一連のコーディングのベストプラクティスに準拠しているかどうかを認識して報告するために、ソフトウェアで静的分析を実行するツールです。

+
+ +

タスクの実行

+ +

依存関係の定義と取得に加えて、package.json ファイルに名前付きスクリプトを定義し、NPM を呼び出してそれらを run-script コマンドで実行することもできます。このアプローチは、実行中のテストや開発の一部を自動化したり、ツールチェーン (たとえば JavaScript の縮小、画像の縮小、コードの LINT/分析などのツールの実行) を構築したりするためによく使用されます。

+ +
+

メモ: GulpGrunt のようなタスクランナーもテストや他の外部ツールを実行するために使うことができます。

+
+ +

たとえば、前のセクションで指定した eslint 開発依存関係を実行するためのスクリプトを定義するには、次のスクリプトブロックを package.json ファイルに追加します (アプリケーションソースが /src/js フォルダにあると仮定します)。

+ +
"scripts": {
+  ...
+  "lint": "eslint src/js"
+  ...
+}
+
+ +

もう少し詳しく説明すると、eslint src/js は、app ディレクトリー内の src/js ディレクトリーに含まれる JavaScript ファイルに対して eslint を実行するために terminal/command 行に入力できるコマンドです。アプリの package.json ファイル内に上記を含めると、このコマンドのショートカット - つまり lint が提供されます。

+ +

こうすれば、NPM を使って eslint を実行することができます。

+ +
npm run-script lint
+# OR (using the alias)
+npm run lint
+
+ +

この例は元のコマンドより短く見えないかもしれませんが、複数のコマンドのチェーンを含めて、npm スクリプト内にもっと大きなコマンドを含めることができます。一度にすべてのテストを実行する単一の npm スクリプトを指定できます。

+ +

Express Application Generator のインストール

+ +

Express Application Generator ツールは Express アプリケーションの「スケルトン」を生成します。次に示すように、NPM を使用してジェネレーターをインストールします (-g フラグを指定すると、ツールをグローバルにインストールして、どこからでも呼び出すことができます)。

+ +
npm install express-generator -g
+ +

デフォルト設定で "helloworld" という名前の Express アプリを作成するには、作成する場所に移動して、図のようにアプリを実行します。

+ +
express helloworld
+ +
+

注記: 利用するテンプレートライブラリ等の他の設定を指定することもできます。すべてのオプションを見るには、help コマンドを使用してください。

+ +
express --help
+
+
+ +

NPM は現在の場所のサブフォルダーに新しい Express アプリケーションを作成し、コンソールにビルドの進行状況を表示します。完了すると、Node の依存関係をインストールしてアプリを起動するために入力する必要があるコマンドがツールに表示されます。

+ +
+

新しいアプリには、そのルートディレクトリーに package.json ファイルがあります。これを開くと、Express やテンプレートライブラリ Jade など、インストールされている依存関係を確認できます。

+ +
{
+  "name": "helloworld",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "start": "node ./bin/www"
+  },
+  "dependencies": {
+    "body-parser": "~1.18.2",
+    "cookie-parser": "~1.4.3",
+    "debug": "~2.6.9",
+    "express": "~4.15.5",
+    "jade": "~1.11.0",
+    "morgan": "~1.9.0",
+    "serve-favicon": "~2.4.5"
+  }
+}
+ + +
+ +

次に示すように、NPM を使用して helloworld アプリのすべての依存関係をインストールします。

+ +
cd helloworld
+npm install
+
+ +

次に、以下のようにアプリを実行します (コマンドは Windows と Linux/macOS で若干異なります)。

+ +
#  Windows のコマンドプロンプトで helloworld を実行
+SET DEBUG=helloworld:* & npm start
+
+# Windows の PowerShell で helloworld を実行
+SET DEBUG=helloworld:* | npm start
+
+# Linux/macOS で helloworld を実行
+DEBUG=helloworld:* npm start
+
+ +

DEBUG コマンドは有用なロギングを作成し、その結果、以下に示すような出力が得られます。

+ +
>SET DEBUG=helloworld:* & npm start
+
+> helloworld@0.0.0 start D:\Github\expresstests\helloworld
+> node ./bin/www
+
+  helloworld:server Listening on port 3000 +0ms
+ +

ブラウザーを開いて http://127.0.0.1:3000/ に移動し、デフォルトの Express ウェルカムページを表示します。

+ +

Express - Generated App Default Screen

+ +

スケルトンアプリケーションの生成に関する記事にアクセスしたら、生成されたアプリケーションについて詳しく説明します。

+ + + +

まとめ

+ +

これで、Express ウェブアプリケーションを作成するために使用できる Node 開発環境がコンピューター上で稼働しています。また、NPM を使用して Express をアプリケーションにインポートする方法、および Express Application Generator ツールを使用してアプリケーションを作成して実行する方法についても説明しました。

+ +

次の記事では、この環境と関連ツールを使って完全なウェブアプリケーションを構築するためのチュートリアルを始めます。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュールの中

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html new file mode 100644 index 0000000000..5c4acc7193 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.html @@ -0,0 +1,89 @@ +--- +title: 著者詳細ページ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page +--- +

著者詳細ページには、指定された Author に関する情報を、その (自動的に生成された) _id フィールド値を使用して識別し、その Author に関連するすべての Book オブジェクトのリストを表示する必要があります。

+ +

Controller

+ +

Open /controllers/authorController.js.

+ +

Add the following lines to the top of the file to import the async and Book modules (these are needed for our author detail page).

+ +
var async = require('async');
+var Book = require('../models/book');
+ +

Find the exported author_detail() controller method and replace it with the following code.

+ +
// Display detail page for a specific Author.
+exports.author_detail = function(req, res, next) {
+
+    async.parallel({
+        author: function(callback) {
+            Author.findById(req.params.id)
+              .exec(callback)
+        },
+        authors_books: function(callback) {
+          Book.find({ 'author': req.params.id },'title summary')
+          .exec(callback)
+        },
+    }, function(err, results) {
+        if (err) { return next(err); } // Error in API usage.
+        if (results.author==null) { // No results.
+            var err = new Error('Author not found');
+            err.status = 404;
+            return next(err);
+        }
+        // Successful, so render.
+        res.render('author_detail', { title: 'Author Detail', author: results.author, author_books: results.authors_books } );
+    });
+
+};
+
+ +

The method uses async.parallel() to query the Author and their associated Book instances in parallel, with the callback rendering the page when (if) both requests complete successfully. The approach is exactly the same as described for the Genre detail page above.

+ +

View

+ +

Create /views/author_detail.pug and copy in the following text.

+ +
extends layout
+
+block content
+
+  h1 Author: #{author.name}
+  p #{author.date_of_birth} - #{author.date_of_death}
+
+  div(style='margin-left:20px;margin-top:20px')
+
+    h4 Books
+
+    dl
+      each book in author_books
+        dt
+          a(href=book.url) #{book.title}
+        dd #{book.summary}
+
+      else
+        p This author has no books.
+
+ +

Everything in this template has been demonstrated in previous sections.

+ +

What does it look like?

+ +

Run the application and open your browser to http://localhost:3000/. Select the All Authors link, then select one of the authors. If everything is set up correctly, your site should look something like the following screenshot.

+ +

Author Detail Page - Express Local Library site

+ +
+

Note: The appearance of the author lifespan dates is ugly! We'll address that in the final challenge in this article.

+
+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html new file mode 100644 index 0000000000..f738902bfb --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/author_list_page/index.html @@ -0,0 +1,85 @@ +--- +title: 著者リストページとジャンルリストページのチャレンジ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page +--- +

The author list page needs to display a list of all authors in the database, with each author name linked to its associated author detail page. The date of birth and date of death should be listed after the name on the same line.

+ +

Controller

+ +

The author list controller function needs to get a list of all Author instances, and then pass these to the template for rendering.

+ +

Open /controllers/authorController.js. Find the exported author_list() controller method near the top of the file and replace it with the following code (the changed code is shown in bold).

+ +
// Display list of all Authors.
+exports.author_list = function(req, res, next) {
+
+  Author.find()
+    .sort([['family_name', 'ascending']])
+    .exec(function (err, list_authors) {
+      if (err) { return next(err); }
+      //Successful, so render
+      res.render('author_list', { title: 'Author List', author_list: list_authors });
+    });
+
+};
+ +

The method uses the model's find(), sort() and exec() functions to return all Author objects sorted by family_name in alphabetic order. The callback passed to the exec() method is called with any errors (or null) as the first parameter, or a list of all authors on success. If there is an error it calls the next middleware function with the error value, and if not it renders the author_list(.pug) template, passing the page title and the list of authors (author_list).

+ +

View

+ +

Create /views/author_list.pug and replace its content with the text below.

+ +
extends layout
+
+block content
+  h1= title
+
+  ul
+    each author in author_list
+      li
+        a(href=author.url) #{author.name}
+        |  (#{author.date_of_birth} - #{author.date_of_death})
+
+    else
+      li There are no authors.
+ +

The view follows exactly the same pattern as our other templates.

+ +

What does it look like?

+ +

Run the application and open your browser to http://localhost:3000/. Then select the All authors link. If everything is set up correctly, the page should look something like the following screenshot.

+ +

Author List Page - Express Local Library site

+ +
+

Note: The appearance of the author lifespan dates is ugly! You can improve this using the same approach as we used for the BookInstance list (adding the virtual property for the lifespan to the Author model). This time, however, there are missing dates, and references to nonexistent properties are ignored unless strict mode is in effect. moment() returns the current time, and you don't want missing dates to be formatted as if they were today. One way to deal with this is to define the body of the function that returns a formatted date so it returns a blank string unless the date actually exists. For example:

+ +

return this.date_of_birth ? moment(this.date_of_birth).format('YYYY-MM-DD') : '';

+
+ +

Genre list page—challenge!Edit

+ +

In this section you should implement your own genre list page. The page should display a list of all genres in the database, with each genre linked to its associated detail page. A screenshot of the expected result is shown below.

+ +

Genre List - Express Local Library site

+ +

The genre list controller function needs to get a list of all Genre instances, and then pass these to the template for rendering.

+ +
    +
  1. You will need to edit genre_list() in /controllers/genreController.js
  2. +
  3. The implementation is almost exactly the same as the author_list() controller. +
      +
    • Sort the results by name, in ascending order.
    • +
    +
  4. +
  5. The template to be rendered should be named genre_list.pug.
  6. +
  7. The template to be rendered should be passed the variables title ('Genre List') and genre_list (the list of genres returned from your Genre.find() callback.
  8. +
  9. The view should match the screenshot/requirements above (this should have a very similar structure/format to the Author list view, except for the fact that genres do not have dates).
  10. +
+ +

Next steps

+ +

Return to Express Tutorial Part 5: Displaying library data.

+ +

Proceed to the next subarticle of part 5: Genre detail page.

diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html new file mode 100644 index 0000000000..f2080e6109 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.html @@ -0,0 +1,112 @@ +--- +title: 本の詳細ページ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page +--- +

The Book detail page needs to display the information for a specific Book, identified using its (automatically generated) _id field value, along with information about each associated copy in the libary (BookInstance). Wherever we display an author, genre, or book instance, these should be linked to the associated detail page for that item.

+ +

Controller

+ +

Open /controllers/bookController.js. Find the exported book_detail() controller method and replace it with the following code.

+ +
// Display detail page for a specific book.
+exports.book_detail = function(req, res, next) {
+
+    async.parallel({
+        book: function(callback) {
+
+            Book.findById(req.params.id)
+              .populate('author')
+              .populate('genre')
+              .exec(callback);
+        },
+        book_instance: function(callback) {
+
+          BookInstance.find({ 'book': req.params.id })
+          .exec(callback);
+        },
+    }, function(err, results) {
+        if (err) { return next(err); }
+        if (results.book==null) { // No results.
+            var err = new Error('Book not found');
+            err.status = 404;
+            return next(err);
+        }
+        // Successful, so render.
+        res.render('book_detail', { title: 'Title', book: results.book, book_instances: results.book_instance } );
+    });
+
+};
+
+
+ +
+

Note: We don't need to require async and BookInstance, as we already imported those modules when we implemented the home page controller.

+
+ +

The method uses async.parallel() to find the Book and its associated copies (BookInstances) in parallel. The approach is exactly the same as described for the Genre detail page above.

+ +

View

+ +

Create /views/book_detail.pug and add the text below.

+ +
extends layout
+
+block content
+  h1 #{title}: #{book.title}
+
+  p #[strong Author:]
+    a(href=book.author.url) #{book.author.name}
+  p #[strong Summary:] #{book.summary}
+  p #[strong ISBN:] #{book.isbn}
+  p #[strong Genre:]&nbsp;
+    each val, index in book.genre
+      a(href=val.url) #{val.name}
+      if index < book.genre.length - 1
+        |,
+
+  div(style='margin-left:20px;margin-top:20px')
+    h4 Copies
+
+    each val in book_instances
+      hr
+      if val.status=='Available'
+        p.text-success #{val.status}
+      else if val.status=='Maintenance'
+        p.text-danger #{val.status}
+      else
+        p.text-warning #{val.status}
+      p #[strong Imprint:] #{val.imprint}
+      if val.status!='Available'
+        p #[strong Due back:] #{val.due_back}
+      p #[strong Id:]&nbsp;
+        a(href=val.url) #{val._id}
+
+    else
+      p There are no copies of this book in the library.
+
+ +

Almost everything in this template has been demonstrated in previous sections.

+ +
+

Note: The list of genres associated with the book is implemented in the template as below. This adds a comma after every genre associated with the book except for the last one.

+ +
  p #[strong Genre:]
+    each val, index in book.genre
+      a(href=val.url) #{val.name}
+      if index < book.genre.length - 1
+        |, 
+
+ +

What does it look like?

+ +

Run the application and open your browser to http://localhost:3000/. Select the All books link, then select one of the books. If everything is set up correctly, your page should look something like the following screenshot.

+ +

Book Detail Page - Express Local Library site

+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html new file mode 100644 index 0000000000..4dfc9c5a5e --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/book_list_page/index.html @@ -0,0 +1,68 @@ +--- +title: ブックリストページ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page +--- +

Next we'll implement our book list page. This page needs to display a list of all books in the database along with their author, with each book title being a hyperlink to its associated book detail page.

+ +

Controller

+ +

The book list controller function needs to get a list of all Book objects in the database, and then pass these to the template for rendering.

+ +

Open /controllers/bookController.js. Find the exported book_list() controller method and replace it with the following code.

+ +
// Display list of all Books.
+exports.book_list = function(req, res, next) {
+
+  Book.find({}, 'title author')
+    .populate('author')
+    .exec(function (err, list_books) {
+      if (err) { return next(err); }
+      //Successful, so render
+      res.render('book_list', { title: 'Book List', book_list: list_books });
+    });
+
+};
+ +

The method uses the model's find() function to return all Book objects, selecting to return only the title and author as we don't need the other fields (it will also return the _id and virtual fields). Here we also call populate() on Book, specifying the author field—this will replace the stored book author id with the full author details.

+ +

On success, the callback passed to the query renders the book_list(.pug) template, passing the title and book_list (list of books with authors) as variables.

+ +

View

+ +

Create /views/book_list.pug and copy in the text below.

+ +
extends layout
+
+block content
+  h1= title
+
+  ul
+    each book in book_list
+      li
+        a(href=book.url) #{book.title}
+        |  (#{book.author.name})
+
+    else
+      li There are no books.
+ +

The view extends the layout.pug base template and overrides the block named 'content'. It displays the title we passed in from the controller (via the render() method) and then iterates through the book_list variable using the each-in-else syntax. A list item is created for each book displaying the book title as a link to the book's detail page followed by the author name. If there are no books in the book_list then the else clause is executed, and displays the text 'There are no books.'

+ +
+

Note: We use book.url to provide the link to the detail record for each book (we've implemented this route, but not the page yet). This is a virtual property of the Book model which uses the model instance's _id field to produce a unique URL path.

+
+ +

Of interest here is that each book is defined as two lines, using the pipe for the second line (highlighted above). This approach is needed because if the author name were on the previous line then it would be part of the hyperlink.

+ +

What does it look like?

+ +

Run the application (see Testing the routes for the relevant commands) and open your browser to http://localhost:3000/. Then select the All books link. If everything is set up correctly, your site should look something like the following screenshot.

+ +

Book List Page - Express Local Library site

+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html new file mode 100644 index 0000000000..3c6cace6a5 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.html @@ -0,0 +1,91 @@ +--- +title: ブックインスタンス詳細ページとチャレンジ +slug: >- + Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge +translation_of: >- + Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge +--- +

BookInstance detail page

+ +

The BookInstance detail page needs to display the information for each BookInstance, identified using its (automatically generated) _id field value. This will include the Book name (as a link to the Book detail page) along with other information in the record.

+ +

Controller

+ +

Open /controllers/bookinstanceController.js. Find the exported bookinstance_detail() controller method and replace it with the following code.

+ +
// Display detail page for a specific BookInstance.
+exports.bookinstance_detail = function(req, res, next) {
+
+    BookInstance.findById(req.params.id)
+    .populate('book')
+    .exec(function (err, bookinstance) {
+      if (err) { return next(err); }
+      if (bookinstance==null) { // No results.
+          var err = new Error('Book copy not found');
+          err.status = 404;
+          return next(err);
+        }
+      // Successful, so render.
+      res.render('bookinstance_detail', { title: 'Book:', bookinstance:  bookinstance});
+    })
+
+};
+
+ +

The method calls BookInstance.findById() with the ID of a specific book instance extracted from the URL (using the route), and accessed within the controller via the request parameters: req.params.id). It then calls populate() to get the details of the associated Book.

+ +

View

+ +

Create /views/bookinstance_detail.pug and copy in the content below.

+ +
extends layout
+
+block content
+
+  h1 ID: #{bookinstance._id}
+
+  p #[strong Title:]
+    a(href=bookinstance.book.url) #{bookinstance.book.title}
+  p #[strong Imprint:] #{bookinstance.imprint}
+
+  p #[strong Status:]
+    if bookinstance.status=='Available'
+      span.text-success #{bookinstance.status}
+    else if bookinstance.status=='Maintenance'
+      span.text-danger #{bookinstance.status}
+    else
+      span.text-warning #{bookinstance.status}
+
+  if bookinstance.status!='Available'
+    p #[strong Due back:] #{bookinstance.due_back}
+
+ +

Everything in this template has been demonstrated in previous sections.

+ +

What does it look like?

+ +

Run the application and open your browser to http://localhost:3000/. Select the All book-instances link, then select one of the items. If everything is set up correctly, your site should look something like the following screenshot.

+ +

BookInstance Detail Page - Express Local Library site

+ +

Challenge

+ +

Currently most dates displayed on the site use the default JavaScript format (e.g. Tue Dec 06 2016 15:49:58 GMT+1100 (AUS Eastern Daylight Time). The challenge for this article is to improve the appearance of the date display for Author lifespan information (date of death/birth) and for BookInstance detail pages to use the format: December 6th, 2016.

+ +
+

Note: You can use the same approach as we used for the Book Instance List (adding the virtual property for the lifespan to the Author model and use moment to format the date strings).

+
+ +

The requirements to meet this challenge:

+ +
    +
  1. Replace the variable due_back with due_back_formatted in the BookInstance detail page.
  2. +
  3. Update the Author module to add a lifespan virtual property. The lifespan should look like: date_of_birth - date_of_death, where both values have the same date format as BookInstance.due_back_formatted.
  4. +
  5. Use Author.lifespan in all views where you currently explicitly use date_of_birth and date_of_death.
  6. +
+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html new file mode 100644 index 0000000000..9bc7ee305f --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.html @@ -0,0 +1,69 @@ +--- +title: ブックインスタンスリストページ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page +--- +

Next we'll implement our list of all book copies (BookInstance) in the library. This page needs to include the title of the Book associated with each BookInstance (linked to its detail page) along with other information in the BookInstance model, including the status, imprint, and unique id of each copy. The unique id text should be linked to the BookInstance detail page.

+ +

Controller

+ +

The BookInstance list controller function needs to get a list of all book instances, populate the associated book information, and then pass the list to the template for rendering.

+ +

Open /controllers/bookinstanceController.js. Find the exported bookinstance_list() controller method and replace it with the following code (the changed code is shown in bold).

+ +
// Display list of all BookInstances.
+exports.bookinstance_list = function(req, res, next) {
+
+  BookInstance.find()
+    .populate('book')
+    .exec(function (err, list_bookinstances) {
+      if (err) { return next(err); }
+      // Successful, so render
+      res.render('bookinstance_list', { title: 'Book Instance List', bookinstance_list: list_bookinstances });
+    });
+
+};
+ +

The method uses the model's find() function to return all BookInstance objects. It then daisy-chains a call to populate() with the book field—this will replace the book id stored for each BookInstance with a full Book document.

+ +

On success, the callback passed to the query renders the bookinstance_list(.pug) template, passing the title and bookinstance_list as variables.

+ +

View

+ +

Create /views/bookinstance_list.pug and copy in the text below.

+ +
extends layout
+
+block content
+  h1= title
+
+  ul
+    each val in bookinstance_list
+      li
+        a(href=val.url) #{val.book.title} : #{val.imprint} -
+        if val.status=='Available'
+          span.text-success #{val.status}
+        else if val.status=='Maintenance'
+          span.text-danger #{val.status}
+        else
+          span.text-warning #{val.status}
+        if val.status!='Available'
+          span  (Due: #{val.due_back} )
+
+    else
+      li There are no book copies in this library.
+ +

This view is much the same as all the others. It extends the layout, replacing the content block, displays the title passed in from the controller, and iterates through all the book copies in bookinstance_list. For each copy we display its status (colour coded) and if the book is not available, its expected return date. One new feature is introduced—we can use dot notation after a tag to assign a class. So span.text-success will be compiled to <span class="text-success"> (and might also be written in Pug as span(class="text-success").

+ +

What does it look like?

+ +

Run the application, open your browser to http://localhost:3000/, then select the All book-instances link. If everything is set up correctly, your site should look something like the following screenshot.

+ +

BookInstance List Page - Express Local Library site

+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html new file mode 100644 index 0000000000..58851991b5 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.html @@ -0,0 +1,60 @@ +--- +title: moment を使用した日付のフォーマット +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment +--- +

The default rendering of dates from our models is very ugly: Tue Dec 06 2016 15:49:58 GMT+1100 (AUS Eastern Daylight Time). In this section we'll show how you can update the BookInstance List page from the previous section to present the due_date field in a more friendly format: December 6th, 2016. 

+ +

The approach we will use is to create a virtual property in our BookInstance model that returns the formatted date. We'll do the actual formatting using moment, a lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.

+ +
+

Note: It is possible to use moment to format the strings directly in our Pug templates, or we could format the string in a number of other places. Using a virtual property allows us to get the formatted date in exactly the same way as we get the due_date currently. 

+
+ +

Install moment

+ +

Enter the following command in the root of the project:

+ +
npm install moment
+ +

Create the virtual property

+ +
    +
  1. Open ./models/bookinstance.js.
  2. +
  3. At the top of the page, import moment. +
    var moment = require('moment');
    +
  4. +
+ +

Add the virtual property due_back_formatted just after the url property.

+ +
BookInstanceSchema
+.virtual('due_back_formatted')
+.get(function () {
+  return moment(this.due_back).format('MMMM Do, YYYY');
+});
+ +
+

Note: The format method can display a date using almost any pattern. The syntax for representing different date components can be found in the moment documentation.

+
+ +

Update the view

+ +

Open /views/bookinstance_list.pug and replace due_back with due_back_formatted.

+ +
      if val.status!='Available'
+        //span  (Due: #{val.due_back} )
+        span  (Due: #{val.due_back_formatted} )       
+ +

That's it. If you go to All book-instances in the sidebar, you should now see all the due dates are far more attractive!

+ +

 

+ +

Next steps

+ + + +

 

diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html new file mode 100644 index 0000000000..0639f79bc3 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.html @@ -0,0 +1,137 @@ +--- +title: async を使用した非同期フロー制御 +slug: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async +--- +

 

+ +

ローカルライブラリのいくつかのコントローラは、特定の順序、あるいは平行して要求される複数の非同期リクエストに依存しています。一般的に、操作の流れを制御し、必要となるすべての情報を取得したときにページを表示するために、asyncモジュール が使用されます。

+ +
+

註: この方法以外にも、近年 JavaScript に導入された Promise などの非同期的な振る舞いや操作の流れを制御する方法があります。

+
+ +

Async はいくつもの有用なメソッドを持っています (このドキュメントを参照してください)。 主要なメソッドをいくつか紹介します。:

+ + + +

Why is this needed?

+ +

Most of the methods we use in Express are asynchronous—you specify an operation to perform, passing a callback. The method returns immediately, and the callback is invoked when the requested operation completes. By convention in Express, callback functions pass an error value as the first parameter (or null on success) and the results from the function (if there are any) as the second parameter.

+ +

If a controller only needs to perform one asynchronous operation to get the information required to render a page then the implementation is easy—we simply render the template in the callback. The code fragment below shows this for a function that renders the count of a model SomeModel (using the Mongoose count() method):

+ +
exports.some_model_count = function(req, res, next) {
+
+  SomeModel.count({ a_model_field: 'match_value' }, function (err, count) {
+    // ... do something if there is an err
+
+    // On success, render the result by passing count into the render function (here, as the variable 'data').
+    res.render('the_template', { data: count } );
+  });
+}
+
+ +

However what if you need to make multiple asynchronous queries, and you can't render the page until all the operations have completed? A naive implementation could "daisy chain" the requests, kicking off subsequent requests in the callback of a previous request, and rendering the response in the final callback. The problem with this approach is that our requests would have to be run in series, even though it might be more efficient to run them in parallel. This could also result in complicated nested code, commonly referred to as callback hell.

+ +

A much better solution would be to execute all the requests in parallel and then have a single callback that executes when all of the queries have completed. This is the sort of flow operation that the Async module makes easy!

+ +

Asynchronous operations in parallel

+ +

The method async.parallel() is used to run multiple asynchronous operations in parallel.

+ +

The first argument to async.parallel() is a collection of the asynchronous functions to run (an array, object or other iterable). Each function is passed a callback(err, result) which it must call on completion with an error err (which can be null) and an optional results value.

+ +

The optional second argument to  async.parallel() is a callback that will be run when all the functions in the first argument have completed. The callback is invoked with an error argument and a result collection that contains the results of the individual asynchronous operations. The result collection is of the same type as the first argument (i.e. if you pass an array of asynchronous functions, the final callback will be invoked with an array of results). If any of the parallel functions reports an error the callback is invoked early (with the error value).

+ +

The example below shows how this works when we pass an object as the first argument. As you can see, the results are returned in an object with the same property names as the original functions that were passed in.

+ +
async.parallel({
+  one: function(callback) { ... },
+  two: function(callback) { ... },
+  ...
+  something_else: function(callback) { ... }
+  },
+  // optional callback
+  function(err, results) {
+    // 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}
+  }
+);
+ +

If you instead pass an array of functions as the first argument, the results will be an array (the array order results will match the original order that the functions were declared—not the order in which they completed).

+ +

Asynchronous operations in series

+ +

The method async.series() is used to run multiple asynchronous operations in sequence, when subsequent functions do not depend on the output of earlier functions. It is essentially declared and behaves in the same way as async.parallel().

+ +
async.series({
+  one: function(callback) { ... },
+  two: function(callback) { ... },
+  ...
+  something_else: function(callback) { ... }
+  },
+  // optional callback after the last asynchronous function completes.
+  function(err, results) {
+    // 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value} 
+  }
+);
+ +
+

Note: The ECMAScript (JavaScript) language specification states that the order of enumeration of an object is undefined, so it is possible that the functions will not be called in the same order as you specify them on all platforms. If the order really is important, then you should pass an array instead of an object, as shown below.

+
+ +
async.series([
+  function(callback) {
+    // do some stuff ...
+    callback(null, 'one');
+  },
+  function(callback) {
+    // do some more stuff ... 
+    callback(null, 'two');
+  }
+ ],
+  // optional callback
+  function(err, results) {
+  // results is now equal to ['one', 'two'] 
+  }
+);
+ +

Dependent asynchronous operations in series

+ +

The method async.waterfall() is used to run multiple asynchronous operations in sequence when each operation is dependent on the result of the previous operation.

+ +

The callback invoked by each asynchronous function contains null for the first argument and results in subsequent arguments. Each function in the series takes the results arguments of the previous callback as the first parameters, and then a callback function. When all operations are complete, a final callback is invoked with the result of the last operation. The way this works is more clear when you consider the code fragment below (this example is from the async documentation):

+ +
async.waterfall([
+  function(callback) {
+    callback(null, 'one', 'two');
+  },
+  function(arg1, arg2, callback) {
+    // arg1 now equals 'one' and arg2 now equals 'two' 
+    callback(null, 'three');
+  },
+  function(arg1, callback) {
+    // arg1 now equals 'three'
+    callback(null, 'done');
+  }
+], function (err, result) {
+  // result now equals 'done'
+}
+);
+ +

Installing async

+ +

Install the async module using the NPM package manager so that we can use it in our code. You do this in the usual way, by opening a prompt in the root of the LocalLibrary project and enter the following command:

+ +
npm install async
+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html new file mode 100644 index 0000000000..40770c5ef2 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.html @@ -0,0 +1,121 @@ +--- +title: ジャンル詳細ページ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page +--- +

The genre detail page needs to display the information for the particular genre instance using its automatically generated _id field value as the identifier. The page should display the genre name, and a list of all books in the genre with links to each book's details page.

+ +

Controller

+ +

Open /controllers/genreController.js and import the async and Book modules at the top of the file.

+ +
var Book = require('../models/book');
+var async = require('async');
+
+ +

Find the exported genre_detail() controller method and replace it with the following code.

+ +
// Display detail page for a specific Genre.
+exports.genre_detail = function(req, res, next) {
+
+    async.parallel({
+        genre: function(callback) {
+            Genre.findById(req.params.id)
+              .exec(callback);
+        },
+
+        genre_books: function(callback) {
+          Book.find({ 'genre': req.params.id })
+          .exec(callback);
+        },
+
+    }, function(err, results) {
+        if (err) { return next(err); }
+        if (results.genre==null) { // No results.
+            var err = new Error('Genre not found');
+            err.status = 404;
+            return next(err);
+        }
+        // Successful, so render
+        res.render('genre_detail', { title: 'Genre Detail', genre: results.genre, genre_books: results.genre_books } );
+    });
+
+};
+
+ +

The method uses async.parallel() to query the genre name and its associated books in parallel, with the callback rendering the page when (if) both requests complete successfully.

+ +

The ID of the required genre record is encoded at the end of the URL and extracted automatically based on the route definition (/genre/:id). The ID is accessed within the controller via the request parameters: req.params.id. It is used in Genre.findById() to get the current genre. It is also used to get all Book objects that have the genre ID in their genre field: Book.find({ 'genre': req.params.id }).

+ +
+

Note: If the genre does not exist in the database (i.e. it may have been deleted) then findById()  will return successfully with no results. In this case we want to display a "not found" page, so we create an Error object and pass it to the next middleware function in the chain. 

+ +
if (results.genre==null) { // No results.
+    var err = new Error('Genre not found');
+    err.status = 404;
+    return next(err);
+}
+
+ +

The message will then propagate through to our error handling code (this was set up when we generated the app skeleton - for more information see Handling Errors).

+
+ +

The rendered view is genre_detail and it is passed variables for the title, genre and the list of books in this genre (genre_books).

+ +

View

+ +

Create /views/genre_detail.pug and fill it with the text below:

+ +
extends layout
+
+block content
+
+  h1 Genre: #{genre.name}
+
+  div(style='margin-left:20px;margin-top:20px')
+
+    h4 Books
+
+    dl
+    each book in genre_books
+      dt
+        a(href=book.url) #{book.title}
+      dd #{book.summary}
+
+    else
+      p This genre has no books
+
+ +

The view is very similar to all our other templates. The main difference is that we don't use the title passed in for the first heading (though it is used in the underlying layout.pug template to set the page title).

+ +

What does it look like?

+ +

Run the application and open your browser to http://localhost:3000/. Select the All genres link, then select one of the genres (e.g. "Fantasy"). If everything is set up correctly, your page should look something like the following screenshot.

+ +

Genre Detail Page - Express Local Library site

+ +
+

You might get an error similar to this:

+ +
Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre"
+
+ +

This is a mongoose error coming from the req.params.id. To solve this problem, first you need to require mongoose on the genreController.js page like this:

+ +
 var mongoose = require('mongoose');
+
+ +

Then use mongoose.Types.ObjectId() to convert the id to a that can be used. For example:

+ +
exports.genre_detail = function(req, res, next) {
+    var id = mongoose.Types.ObjectId(req.params.id);
+    ...
+
+
+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/home_page/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/home_page/index.html new file mode 100644 index 0000000000..3e2f337370 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/home_page/index.html @@ -0,0 +1,133 @@ +--- +title: ホームページ +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page +--- +

The first page we'll create will be the website home page, which is accessible from either the site ('/') or catalog (catalog/) root. This will display some static text describing the site, along with dynamically calculated "counts" of different record types in the database.

+ +

We've already created a route for the home page. In order to complete the page we need to update our controller function to fetch "counts" of records from the database, and create a view (template) that we can use to render the page.

+ +

Route

+ +

We created our index page routes in a previous tutorial. As a reminder, all the route functions are defined in /routes/catalog.js:

+ +
// GET catalog home page.
+router.get('/', book_controller.index);  //This actually maps to /catalog/ because we import the route with a /catalog prefix
+ +

Where the callback function parameter (book_controller.index) is defined in /controllers/bookController.js:

+ +
exports.index = function(req, res, next) {
+    res.send('NOT IMPLEMENTED: Site Home Page');
+}
+ +

It is this controller function that we extend to get information from our models and then render it using a template (view).

+ +

Controller

+ +

The index controller function needs to fetch information about how many Book, BookInstance, available BookInstance, Author, and Genre records we have in the database, render this data in a template to create an HTML page, and then return it in an HTTP response.

+ +
+

Note: We use the countDocuments() method to get the number of instances of each model. This is called on a model with an optional set of conditions to match against in the first argument and a callback in the second argument (as discussed in Using a Database (with Mongoose), and you can also return a Query and then execute it with a callback later. The callback will be returned when the database returns the count, with an error value (or null) as the first parameter and the count of records (or null if there was an error) as the second parameter.

+ +
SomeModel.countDocuments({ a_model_field: 'match_value' }, function (err, count) {
+ // ... do something if there is an err
+ // ... do something with the count if there was no error
+ });
+
+ +

Open /controllers/bookController.js. Near the top of the file you should see the exported index() function.

+ +
var Book = require('../models/book')
+
+exports.index = function(req, res, next) {
+ res.send('NOT IMPLEMENTED: Site Home Page');
+}
+ +

Replace all the code above with the following code fragment. The first thing this does is import (require()) all the models (highlighted in bold). We need to do this because we'll be using them to get our counts of records. It then imports the async module.

+ +
var Book = require('../models/book');
+var Author = require('../models/author');
+var Genre = require('../models/genre');
+var BookInstance = require('../models/bookinstance');
+
+var async = require('async');
+
+exports.index = function(req, res) {
+
+    async.parallel({
+        book_count: function(callback) {
+            Book.countDocuments({}, callback); // Pass an empty object as match condition to find all documents of this collection
+        },
+        book_instance_count: function(callback) {
+            BookInstance.countDocuments({}, callback);
+        },
+        book_instance_available_count: function(callback) {
+            BookInstance.countDocuments({status:'Available'}, callback);
+        },
+        author_count: function(callback) {
+            Author.countDocuments({}, callback);
+        },
+        genre_count: function(callback) {
+            Genre.countDocuments({}, callback);
+        }
+    }, function(err, results) {
+        res.render('index', { title: 'Local Library Home', error: err, data: results });
+    });
+};
+ +

The async.parallel() method is passed an object with functions for getting the counts for each of our models. These functions are all started at the same time. When all of them have completed the final callback is invoked with the counts in the results parameter (or an error).

+ +

On success the callback function calls res.render(), specifying a view (template) named 'index' and an object containing the data that is to be inserted into it (this includes the results object that contains our model counts). The data is supplied as key-value pairs, and can be accessed in the template using the key.

+ +
+

Note: The callback function from async.parallel() above is a little unusual in that we render the page whether or not there was an error (normally you might use a separate execution path for handling the display of errors).

+
+ +

View

+ +

Open /views/index.pug and replace its content with the text below.

+ +
extends layout
+
+block content
+  h1= title
+  p Welcome to #[em LocalLibrary], a very basic Express website developed as a tutorial example on the Mozilla Developer Network.
+
+  h1 Dynamic content
+
+  if error
+    p Error getting dynamic content.
+  else
+    p The library has the following record counts:
+
+    ul
+      li #[strong Books:] !{data.book_count}
+      li #[strong Copies:] !{data.book_instance_count}
+      li #[strong Copies available:] !{data.book_instance_available_count}
+      li #[strong Authors:] !{data.author_count}
+      li #[strong Genres:] !{data.genre_count}
+ +

The view is straightforward. We extend the layout.pug base template, overriding the block named 'content'. The first h1 heading will be the escaped text for the title variable that was passed into the render() function—note the use of the 'h1=' so that the following text is treated as a JavaScript expression. We then include a paragraph introducing the LocalLibrary.

+ +

Under the Dynamic content heading we check whether the error variable passed in from the render() function has been defined. If so, we note the error. If not, we get and list the number of copies of each model from the data variable.

+ +
+

Note: We didn't escape the count values (i.e. we used the !{} syntax) because the count values are calculated. If the information was supplied by end-users then we'd escape the variable for display.

+
+ +

What does it look like?

+ +

At this point we should have created everything needed to display the index page. Run the application and open your browser to http://localhost:3000/. If everything is set up correctly, your site should look something like the following screenshot.

+ +

Home page - Express Local Library site

+ +
+

Note: You won't be able to use the sidebar links yet because the urls, views, and templates for those pages haven't been defined. If you try you'll get errors like "NOT IMPLEMENTED: Book list" for example, depending on the link you click on.  These string literals (which will be replaced with proper data) were specified in the different controllers that live inside your "controllers" file.

+
+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/index.html new file mode 100644 index 0000000000..5726b6c0e1 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/index.html @@ -0,0 +1,92 @@ +--- +title: 'Express チュートリアル Part 5: ライブラリデータの表示' +slug: Learn/Server-side/Express_Nodejs/Displaying_data +tags: + - Express + - nodejs + - pug + - コントローラ + - テンプレート + - ビュー + - 初心者 + - 学習 +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}
+ +

これで地域図書館の Web サイトの書籍やその他のデータを表示するページを追加する準備が整いました。このページには、各モデルタイプのレコード数と、すべてのモデルのリストおよび詳細ページを示すホームページが含まれます。その過程で、データベースからレコードを取得したり、テンプレートを使用したりする際の実際的な経験を積むことになります。

+ + + + + + + + + + + + +
前提条件:以前のチュートリアルのトピック (Express チュートリアル Part 4: ルートとコントローラを含む) を完了してください。
目標:async モジュールと Pug テンプレート言語の使い方、そしてコントローラ関数の URL からデータを取得する方法を理解すること。
+ +

概要

+ +

前回のチュートリアル記事では、データベースとやり取りするために使用できる Mongoose モデルを定義し、いくつかの初期ライブラリレコードを作成しました。その後、LocalLibrary Web サイトに必要なすべてのルートを作成しましたが、"ダミーコントローラ" 関数 (ページにアクセスすると "未実装" のメッセージを返すだけのスケルトンコントローラ関数) を使用しました。

+ +

次のステップは、私たちの図書館情報を表示するページに適切な実装をすることです (後の記事で情報を作成、更新、または削除するフォームを特徴とする実装ページを見ます)。これには、モデルを使用してレコードを取得するためのコントローラ機能の更新、およびこの情報をユーザに表示するためのテンプレートの定義が含まれます。

+ +

はじめに、コントローラ関数で非同期操作を管理する方法と Pug を使用してテンプレートを作成する方法を説明する概要/入門トピックを提供します。それから、主要な "読み取り専用" ページのそれぞれに、それらが使用する特別な機能や新しい機能についての簡単な説明を付けて実装を提供します。

+ +

この記事が終わるときには、ルート、非同期関数、ビュー、およびモデルが実際にどのように機能するのかについてのエンドツーエンドの理解が十分にあるはずです。

+ +

ライブラリデータチュートリアルサブ記事の表示

+ +

次のサブ記事では、必要な Web サイトページを表示するために必要なさまざまな機能を追加するプロセスについて説明します。次のものに進む前に、順番にこれらのそれぞれを読み、作業する必要があります。

+ +
    +
  1. async を使用した非同期フロー制御
  2. +
  3. テンプレートプライマー
  4. +
  5. LocalLibrary 基本テンプレート
  6. +
  7. ホームページ
  8. +
  9. ブックリストページ
  10. +
  11. ブックインスタンスリストページ
  12. +
  13. moment を使用した日付のフォーマット
  14. +
  15. 著者リストページとジャンルリストページのチャレンジ
  16. +
  17. ジャンル詳細ページ
  18. +
  19. 本の詳細ページ
  20. +
  21. 著者詳細ページ
  22. +
  23. ブックインスタンス詳細ページとチャレンジ
  24. +
+ +

まとめ

+ +

これで、サイトのすべての "読み取り専用" ページを作成しました。各モデルのインスタンスの数を表示するホームページ、および書籍、書籍のインスタンス、作家、ジャンルのリストと詳細ページです。その過程でコントローラ、非同期操作を使用したときのフロー制御の管理、Pug を使用したビューの作成、モデルを使用したデータベースの照会、ビューからテンプレートへの情報の受け渡し方法などについて多くの基礎知識を得て、そしてテンプレートを作成および拡張しました。チャレンジを完了した人たちはまた、moment を使った日付処理について少し学んだことでしょう。

+ +

次回の記事では、サイトに格納されているデータの変更を開始するための HTML フォームとフォーム処理コードを作成して、私たちの知識に基づいて構築します。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html new file mode 100644 index 0000000000..a97c536eb2 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.html @@ -0,0 +1,69 @@ +--- +title: LocalLibrary 基本テンプレート +slug: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template +--- +

Now that we understand how to extend templates using Pug, let's start by creating a base template for the project. This will have a sidebar with links for the pages we hope to create across the tutorial articles (e.g. to display and create books, genres, authors, etc.) and a main content area that we'll override in each of our individual pages.

+ +

Open /views/layout.pug and replace the content with the code below.

+ +
doctype html
+html(lang='en')
+  head
+    title= title
+    meta(charset='utf-8')
+    meta(name='viewport', content='width=device-width, initial-scale=1')
+    link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css')
+    script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js')
+    script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js')
+    link(rel='stylesheet', href='/stylesheets/style.css')
+  body
+    div(class='container-fluid')
+      div(class='row')
+        div(class='col-sm-2')
+          block sidebar
+            ul(class='sidebar-nav')
+              li
+                a(href='/catalog') Home
+              li
+                a(href='/catalog/books') All books
+              li
+                a(href='/catalog/authors') All authors
+              li
+                a(href='/catalog/genres') All genres
+              li
+                a(href='/catalog/bookinstances') All book-instances
+              li
+                hr
+              li
+                a(href='/catalog/author/create') Create new author
+              li
+                a(href='/catalog/genre/create') Create new genre
+              li
+                a(href='/catalog/book/create') Create new book
+              li
+                a(href='/catalog/bookinstance/create') Create new book instance (copy)
+
+        div(class='col-sm-10')
+          block content
+ +

The template uses (and includes) JavaScript and CSS from Bootstrap to improve the layout and presentation of the HTML page. Using Bootstrap or another client-side web framework is a quick way to create an attractive page that can scale well on different browser sizes, and it also allows us to deal with the page presentation without having to get into any of the details—we just want to focus on the server-side code here!

+ +

The layout should be fairly obvious if you've read our above Template primer. Note the use of block content as a placeholder for where the content for our individual pages will be placed.

+ +

The base template also references a local css file (style.css) that provides a little additional styling. Open /public/stylesheets/style.css and replace its content with the following CSS code:

+ +
.sidebar-nav {
+    margin-top: 20px;
+    padding: 0;
+    list-style: none;
+}
+ +

When we get round to running our site, we should see the sidebar appear! In the next sections we will use the above layout to define the individual pages.

+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/displaying_data/template_primer/index.html b/files/ja/learn/server-side/express_nodejs/displaying_data/template_primer/index.html new file mode 100644 index 0000000000..a68921a6a7 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/displaying_data/template_primer/index.html @@ -0,0 +1,149 @@ +--- +title: テンプレートプライマー +slug: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer +translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer +--- +

A template is a text file defining the structure or layout of an output file, with placeholders used to represent where data will be inserted when the template is rendered (in Express, templates are referred to as views).

+ +

Express template choices

+ +

Express can be used with many different template rendering engines. In this tutorial we use Pug (formerly known as Jade) for our templates. This is the most popular Node template language, and describes itself as a "clean, whitespace-sensitive syntax for writing HTML, heavily influenced by Haml".

+ +

Different template languages use different approaches for defining the layout and marking placeholders for data—some use HTML to define the layout while others use different markup formats that can be compiled to HTML. Pug is of the second type; it uses a representation of HTML where the first word in any line usually represents an HTML element, and indentation on subsequent lines is used to represent any content nested within those elements. The result is a page definition that translates directly to HTML, but is arguably more concise and easier to read.

+ +
+

Note: The downside of using Pug is that it is sensitive to indentation and whitespace (if you add an extra space in the wrong place you may get an unhelpful error code). However once you have your templates in place, they are very easy to read and maintain.

+
+ +

Template configuration

+ +

The LocalLibrary was configured to use Pug when we created the skeleton website. You should see the pug module included as a dependency in the website's package.json file, and the following configuration settings in the app.js file. The settings tell us that we're using pug as the view engine, and that Express should search for templates in the /views subdirectory.

+ +
// View engine setup.
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'pug');
+ +

If you look in the views directory you will see the .pug files for the project's default views. These include the view for the home page (index.pug) and base template (layout.pug) that we will need to replace with our own content.

+ +
/express-locallibrary-tutorial  //the project root
+  /views
+    error.pug
+    index.pug
+    layout.pug
+
+ +

Template syntax

+ +

The example template file below shows off many of Pug's most useful features.

+ +

The first thing to notice is that the file maps the structure of a typical HTML file, with the first word in (almost) every line being an HTML element, and indentation being used to indicate nested elements. So for example, the body element is inside an html element, and paragraph elements (p) are within the body element, etc. Non-nested elements (e.g. individual paragraphs) are on separate lines.

+ +
doctype html
+html(lang="en")
+  head
+    title= title
+    script(type='text/javascript').
+  body
+    h1= title
+
+    p This is a line with #[em some emphasis] and #[strong strong text] markup.
+    p This line has un-escaped data: !{'<em> is emphasised</em>'} and escaped data: #{'<em> is not emphasised</em>'}.
+      | This line follows on.
+    p= 'Evaluated and <em>escaped expression</em>:' + title
+
+    <!-- You can add HTML comments directly -->
+    // You can add single line JavaScript comments and they are generated to HTML comments
+    //- Introducing a single line JavaScript comment with "//-" ensures the comment isn't rendered to HTML
+
+    p A line with a link
+      a(href='/catalog/authors') Some link text
+      |  and some extra text.
+
+    #container.col
+      if title
+        p A variable named "title" exists.
+      else
+        p A variable named "title" does not exist.
+      p.
+        Pug is a terse and simple template language with a
+        strong focus on performance and powerful features.
+
+    h2 Generate a list
+
+    ul
+      each val in [1, 2, 3, 4, 5]
+        li= val
+ +

Element attributes are defined in parentheses after their associated element. Inside the parentheses, the attributes are defined in comma- or whitespace- separated lists of the pairs of attribute names and attribute values, for example:

+ + + +

The values of all attributes are escaped (e.g. characters like ">" are converted to their HTML code equivalents like "&gt;") to prevent injection of JavaScript/cross-site scripting attacks.

+ +

If a tag is followed by the equals sign, the following text is treated as a JavaScript expression. So for example, in the first line below, the content of the h1 tag will be variable title (either defined in the file or passed into the template from Express). In the second line the paragraph content is a text string concatented with the title variable. In both cases the default behaviour is to escape the line.

+ +
h1= title
+p= 'Evaluated and <em>escaped expression</em>:' + title
+ +

If there is no equals symbol after the tag then the content is treated as plain text. Within the plain text you can insert escaped and unescaped data using the #{} and !{} syntax, as shown below. You can also add raw HTML within the plain text.

+ +
p This is a line with #[em some emphasis] and #[strong strong text] markup.
+p This line has an un-escaped string: !{'<em> is emphasised</em>'}, an escaped string: #{'<em> is not emphasised</em>'}, and escaped variables: #{title}.
+ +
+

Tip: You will almost always want to escape data from users (via the #{} syntax). Data that can be trusted (e.g. generated counts of records, etc.) may be displayed without escaping the values.

+
+ +

You can use the pipe ('|') character at the beginning of a line to indicate "plain text". For example, the additional text shown below will be displayed on the same line as the preceding anchor, but will not be linked.

+ +
a(href='http://someurl/') Link text
+| Plain text
+ +

Pug allows you to perform conditional operations using if, else , else if and unless—for example:

+ +
if title
+  p A variable named "title" exists
+else
+  p A variable named "title" does not exist
+ +

You can also perform loop/iteration operations using each-in or while syntax. In the code fragment below we've looped through an array to display a list of variables (note the use of the 'li=' to evaluate the "val" as a variable below. The value you iterate across can also be passed into the template as a variable!

+ +
ul
+  each val in [1, 2, 3, 4, 5]
+    li= val
+ +

The syntax also supports comments (that can be rendered in the output—or not—as you choose), mixins to create reusable blocks of code, case statements, and many other features. For more detailed information see The Pug docs.

+ +

Extending templates

+ +

Across a site, it is usual for all pages to have a common structure, including standard HTML markup for the head, footer, navigation, etc. Rather than forcing developers to duplicate this "boilerplate" in every page, Pug allows you to declare a base template and then extend it, replacing just the bits that are different for each specific page.

+ +

For example, the base template layout.pug created in our skeleton project looks like this:

+ +
doctype html
+html
+  head
+    title= title
+    link(rel='stylesheet', href='/stylesheets/style.css')
+  body
+    block content
+ +

The block tag is used to mark up sections of content that may be replaced in a derived template (if the block is not redefined then its implementation in the base class is used).

+ +

The default index.pug (created for our skeleton project) shows how we override the base template. The extends tag identifies the base template to use, and then we use block section_name to indicate the new content of the section that we will override.

+ +
extends layout
+
+block content
+  h1= title
+  p Welcome to #{title}
+ +

Next steps

+ + diff --git a/files/ja/learn/server-side/express_nodejs/forms/index.html b/files/ja/learn/server-side/express_nodejs/forms/index.html new file mode 100644 index 0000000000..ea3d378895 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/forms/index.html @@ -0,0 +1,263 @@ +--- +title: 'Express チュートリアル Part 6: フォームの操作' +slug: Learn/Server-side/Express_Nodejs/forms +translation_of: Learn/Server-side/Express_Nodejs/forms +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}
+ +

このチュートリアルでは、Pug を使用して Express で HTML フォームを操作する方法、特にデータベースからドキュメントを作成、更新、削除するためのフォームを作成する方法を説明します。

+ + + + + + + + + + + + +
前提条件:Express チュートリアル Part 5: ライブラリデータの表示など、これまでのチュートリアルのトピックをすべて完了してください。
目標:ユーザからデータを取得するためのフォームの作成方法を理解し、このデータでデータベースを更新する。
+ +

概要

+ +

HTMLフォームとは、サーバーに送信するためにユーザーから情報を収集するために使用できる Web ページ上の 1 つ以上のフィールド/ウィジェットのグループのことです。テキストボックス、チェックボックス、ラジオボタン、日付選択など、さまざまなタイプのデータを入力するのに適したフォーム入力が用意されているので、フォームを使えばユーザーからの入力を柔軟に収集することが出来ます。また、フォームはサーバとデータを共有するための比較的安全な方法でもあり、クロスサイトリクエストフォージェリ保護機能を使ってPOSTリクエストでデータを送信することができます。

+ +

フォームを扱うのは複雑です。開発者はフォーム用の HTML を書き、サーバー上で入力されたデータを検証して特殊文字を置換し、無効なフィールドをユーザーに知らせるためにエラーメッセージを表示してフォームを再度表示し、送信が成功したときにデータを処理し、最後に成功を示す何らかの方法でユーザーに応答しなければなりません。

+ +

このチュートリアルでは、上記の操作をExpressで実行する方法を紹介します。途中で、サンプルとして地域図書館のウェブサイトを拡張して、ユーザーがライブラリからアイテムを作成、編集、削除できるようにします。

+ +
+

Note: サンプルとして準備されている地域図書館のウェブサイトは認証済みユーザのみに閲覧を制限する方法については書いてないので、現時点ではどのユーザでもデータベースに変更を加えることができます。

+
+ +

HTMLフォーム

+ +

最初にHTMLフォームの簡単な概要を説明します。ある「チーム」の名前とそれに関連するラベルを入力するための単一のテキストフィールドを持つシンプルな HTML フォームを考えてみましょう。

+ +

Simple name field example in HTML form

+ +

フォームは HTML で <form>...</form> タグ内の要素の集合として定義され、type="submit" input要素を少なくとも 1 つ含みます。

+ +
<form action="/team_name_url/" method="post">
+    <label for="team_name">名前を入力してください: </label>
+    <input id="team_name" type="text" name="name_field" value="デフォルトのチーム名.">
+    <input type="submit" value="OK">
+</form>
+ +

ここではチーム名を入力するための1つのテキストフィールドだけを含んでいますが、フォームは他の入力要素とそれに関連したラベルをいくつでも含むことができます。フィールドのtype属性はどのような種類のウィジェットが表示されるかを定義します。フィールドのnameidはJavaScript/CSS/HTMLでフィールドを識別するために使われ、valueはフィールドが最初に表示されるときの初期値を定義します。マッチングするチームのラベルは、labelタグ(上記の「名前を入力してください」を参照)を使用して指定され、forフィールドには関連するinputタグのid値が含まれます。

+ +

submit inputタグは標準ではボタンとして表示されます。このボタンは、他のinput要素に含まれるデータをサーバーにアップロードするためにユーザーが押すことができます(この例だとteam_nameだけ)。フォーム属性はデータを送信するために使用されるHTTP methodとサーバー上のデータの送信先(action)を定義します。

+ + + +

フォーム処理工程

+ +

フォームの処理はモデルに関する情報を表示するために学んだのと同じテクニックをすべて使います: ルートはリクエストをコントローラ関数に送り、モデルからのデータの読み込みを含む必要なデータベースアクションを実行し、HTMLページを生成して返します。さらに複雑なのは、サーバーがユーザーによって提供されたデータを処理し、何か問題があればエラー情報とともにフォームを再表示する必要があるということです。

+ +

フォームを含むページのリクエスト(緑色で示されている)から始まる、フォームリクエストを処理するためのプロセスフローチャートを以下に示す。

+ +

上の図のように、フォーム処理のコードが必要とする主なものは以下の通りです。

+ +
    +
  1. ユーザーが最初に要求したときにデフォルトのフォームを表示します。 +
      +
    • フォームには空白のフィールドが含まれていたり (新しいレコードを作成している場合など)、初期値があらかじめ入力されていたり (レコードを変更している場合や、デフォルトの初期値がある場合など) します。
    • +
    +
  2. +
  3. ユーザーから送信されたデータを、通常はHTTP POSTリクエストで受信します。
  4. +
  5. データを検証し、ハッキング防止のために特殊文字を置換(サニタイズ)します。
  6. +
  7. データが無効な場合は、ユーザーが入力した値と問題のあるフィールドのエラーメッセージをフォームに再表示します。
  8. +
  9. すべてのデータが有効な場合、必要なアクションを実行します(例:データベースにデータを保存する、通知メールを送信する、検索結果を返す、ファイルをアップロードするなど)。
  10. +
  11. すべてのアクションが完了したら、ユーザーを別のページにリダイレクトします。
  12. +
+ +

多くの場合、フォーム処理コードは、フォームの初期表示のためのGETルートと、フォームデータの検証と処理のための同じパスへのPOSTルートを使用して実装されています。これがこのチュートリアルで使用されるアプローチです。

+ +

Express 自体はフォーム操作のための特別なサポートを提供していませんが、ミドルウェアを使用してフォームからの POSTGET パラメータを処理したり、それらの値を検証/サニタイズしたりすることができます。

+ +

検証とサニタイズ

+ +

フォームからのデータが保存される前に、それは検証され、サニタイズされなければなりません。

+ + + +

このチュートリアルでは、人気のある express-validator モジュールを使ってフォームデータの検証とサニタイズを行います。

+ +

インストール

+ +

プロジェクトのルートで以下のコマンドを実行してモジュールをインストールします。

+ +
npm install express-validator
+
+ +

express-validatorの使用

+ +
+

Note: Githubのexpress-validatorガイドにAPIの概要が書かれています。(カスタムバリデータの作成を含む) すべての機能を知るには、これを読むことをお勧めします。以下では、サンプルの「地域図書館」にとって有用なサブセットだけを取り上げます。

+
+ +

コントローラでバリデータを使うには、以下のように 'express-validator/check' と 'express-validator/filter'モジュールから使いたい関数を要求(require)しなければなりません。

+ +
const { body,validationResult } = require('express-validator/check');
+const { sanitizeBody } = require('express-validator/filter');
+
+ +

多くの関数が用意されており、リクエストパラメータ、body、ヘッダー、Cookieなどのデータをチェックしてサニタイズすることができますし、一度にすべてのデータをチェックしてサニタイズすることもできます。このチュートリアルでは、主にbodysanitizeBodyvalidationResultを使用します。

+ +

機能は以下のように定義されています。

+ + + +

The validation and sanitization chains are middleware that should be passed to the Express route handler (we do this indirectly, via the controller). When the middleware runs, each validator/sanitizer is run in the order specified.

+ +

We'll cover some real examples when we implement the LocalLibrary forms below.

+ +

Form design

+ +

Many of the models in the library are related/dependent—for example, a Book requires an Author, and may also have one or more Genres. This raises the question of how we should handle the case where a user wishes to:

+ + + +

For this project we will simplify the implementation by stating that a form can only:

+ + + +
+

Note: A more "robust" implementation might allow you to create the dependent objects when creating a new object, and delete any object at any time (for example, by deleting dependent objects, or by removing references to the deleted object from the database).

+
+ +

Routes

+ +

In order to implement our form handling code, we will need two routes that have the same URL pattern. The first (GET) route is used to display a new empty form for creating the object. The second route (POST) is used for validating data entered by the user, and then saving the information and redirecting to the detail page (if the data is valid) or redisplaying the form with errors (if the data is invalid).

+ +

We have already created the routes for all our model's create pages in /routes/catalog.js (in a previous tutorial). For example, the genre routes are shown below:

+ +
// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
+router.get('/genre/create', genre_controller.genre_create_get);
+
+// POST request for creating Genre.
+router.post('/genre/create', genre_controller.genre_create_post);
+
+ +

Express forms subarticles

+ +

The following sub articles will take us through the process of adding the required forms to our example application. You need to read and work through each one in turn, before moving on to the next one.

+ +
    +
  1. Create Genre form — Defining a page to create Genre objects.
  2. +
  3. Create Author form — Defining a page to create Author objects.
  4. +
  5. Create Book form — Defining a page/form to create Book objects.
  6. +
  7. Create BookInstance form — Defining a page/form to create BookInstance objects.
  8. +
  9. Delete Author form — Defining a page to delete Author objects.
  10. +
  11. Update Book form — Defining page to update Book objects.
  12. +
+ +

Challenge yourself

+ +

Implement the delete pages for the Book, BookInstance, and Genre models, linking them from the associated detail pages in the same way as our Author delete page. The pages should follow the same design approach:

+ + + +

A few tips:

+ + + +

Implement the update pages for the BookInstance, Author, and Genre models, linking them from the associated detail pages in the same way as our Book update page.

+ +

A few tips:

+ + + +

まとめ

+ +

NPM の Express、Node、およびサードパーティのパッケージは、Web サイトにフォームを追加するために必要なすべてを提供します。この記事では、Pug を使用してフォームを作成する方法、express-validator を使用して入力を検証およびサニタイズする方法、およびデータベース内のレコードを追加、削除、および変更する方法を学びました。

+ +

これで、基本的なフォームとフォーム処理コードを自分の Node Web サイトに追加する方法を理解したはずです。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/index.html b/files/ja/learn/server-side/express_nodejs/index.html new file mode 100644 index 0000000000..a7ddf93fe5 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/index.html @@ -0,0 +1,79 @@ +--- +title: Express Web フレームワーク (Node.js/JavaScript) +slug: Learn/Server-side/Express_Nodejs +tags: + - Beginner + - CodingScripting + - Express + - Express.js + - Intro + - JavaScript + - Learn + - Node + - Server-side programming + - node.js + - イントロダクション + - サーバサイドプログラミング + - 初心者 + - 学習 +translation_of: Learn/Server-side/Express_Nodejs +--- +
{{LearnSidebar}}
+ +

Express は、JavaScript で書かれ、Node.js 実行環境内でホストされている、人気の指図しないウェブフレームワークです。このモジュールでは、このフレームワークの主な利点、開発環境の設定方法、一般的なウェブ開発と配置作業の実行方法について説明します。

+ +

前提条件

+ +

このモジュールを始める前に、サーバーサイドのウェブプログラミングとウェブフレームワークが何かを理解する必要があります。理想的なのはサーバーサイドのウェブサイトプログラミングの第一歩モジュールのトピックを読むことです。プログラミングの概念と JavaScript の一般的な知識があることを強くお勧めしますが、中核となる概念を理解するために不可欠ではありません。

+ +
+

メモ: このウェブサイトには、クライアントサイド開発のコンテキストで JavaScript を学習するための多くの有用なリソースがあります。JavaScriptJavaScript ガイドJavaScript の基本JavaScript (学習)。JavaScript のコアとなる言語と概念は、Node.js でのサーバーサイド開発と同じであり、この資料は関連性があります。Node.js は、ブラウザーレス環境で役立つ機能をサポートするための追加の API を提供します (たとえば、HTTP サーバーを作成してファイルシステムにアクセスするため。ただし、ブラウザーおよび DOM を操作するための JavaScript API はサポートしません)。

+ +

このガイドは Node.js と Express を使った作業についての情報を提供します。インターネットや本には他にもたくさんの優れたリソースがあります。これらのうち一部は How do I get started with Node.js (StackOverflow) と What are the best resources for learning Node.js? (Quora) からリンクされています。

+
+ +

ガイド

+ +
+
Express/Node の入門
+
この最初の Express 記事では、"Node とは何ですか?"、"Express とは何ですか?" という質問に答えます。Express ウェブフレームワークが特別になった理由の概要を説明します。主な機能の概要を説明し、Express アプリケーションの主な構成要素をいくつか紹介します (ただし、現時点ではテスト用の開発環境はまだありません)。
+
Node (Express) 開発環境の設定
+
Express の目的がわかったので、Windows、Linux (Ubuntu)、および Mac OS X 上で Node/Express 開発環境を設定およびテストする方法を説明します。この記事は、オペレーティングシステム共通の、Express アプリの開発を始めるために必要なものを提供します。
+
Express チュートリアル: 地域図書館のウェブサイト
+
私たちの実用的なチュートリアルシリーズの最初の記事はあなたが何を学ぶかについて説明して、それを通して研究し、そしてその後の記事で進化するであろう "地域図書館" の例のウェブサイトの概要を提供します。
+
Express チュートリアル Part 2: スケルトンウェブサイトの作成
+
この記事ではどのようにして "スケルトン"ウェブサイトプロジェクトを作成し、その後サイト固有のルート、テンプレート/ビュー、およびデータベースを追加するかを説明します。
+
Express チュートリアル Part 3: データベースの使用 (Mongoose を使用)
+
この記事では、Node/Express 用のデータベースについて簡単に紹介します。続いて、Mongoose を使用して「地域図書館」ウェブサイトへのデータベースアクセスを提供する方法を説明します。オブジェクトスキーマとモデルの宣言方法、主なフィールドタイプ、および基本的な検証について説明します。また、モデルデータにアクセスするための主な方法のいくつかについても簡単に説明します。
+
Express チュートリアル Part 4: ルートとコントローラー
+
このチュートリアルでは、「地域図書館」ウェブサイトで最終的に必要なすべてのリソースエンドポイントに対して、"ダミー" ハンドラ関数を使用してルート (URL 処理コード) を設定します。完成したら、次の記事で実際のハンドラ関数を使って拡張できるように、ルート処理コードのためのモジュール構造を作ります。Express を使用してモジュラールートを作成する方法についても、非常によく理解できるでしょう。
+
Express チュートリアル Part 5: ライブラリデータの表示
+
これで、「地域図書館」のウェブサイトの書籍やその他のデータを表示するページを追加する準備が整いました。このページには、各モデルタイプのレコード数と、すべてのモデルのリストおよび詳細ページを示すホームページが含まれます。その過程で、データベースからレコードを取得したりテンプレートを使用したりする実際的な経験を積むことになります。
+
Express チュートリアル Part 6: フォームの操作
+
このチュートリアルでは Pug を使用して Express で HTML フォームを操作する方法、特にデータベースからドキュメントを作成、更新、削除するためのフォームを作成する方法を説明します。
+
Express チュートリアル Part 7: プロダクションへのデプロイ
+
これで素晴らしい「地域図書館」ウェブサイトを作成したので、それを公共のウェブサーバーにインストールして、図書館のスタッフとメンバーがインターネットを介してアクセスできるようにします。この記事では、ウェブサイトをデプロイするためのホストを見つける方法、およびサイトを運用に向けて準備するために必要な作業の概要について説明します。
+
+ +

関連情報

+ +
+
PWS/Cloud Foundry に LocalLibrary をインストールする
+
この記事では、Pivotal Web サービスの PaaS クラウドに「地域図書館」をインストールする方法の実際的なデモンストレーションを行います。これは、上記のチュートリアルの第7部で使用されている PaaS クラウドサービスである Heroku に代わるフル機能のオープンソースです。PWS/Cloud Foundry は、Heroku (または別の PaaS クラウドサービス) に代わるものを探している場合、または単に何か違うことを試したい場合には、絶対にチェックする価値があります。
+
+ +

チュートリアルを追加する

+ +
+

既存のチュートリアル記事は以上となります。あなたがそれを拡張したいならば、カバーする他の興味深いトピックは以下の通りです:

+ + + +

そしてもちろん、評価作業を行うのは素晴らしいことです。

+
diff --git a/files/ja/learn/server-side/express_nodejs/installing_on_pws_cloud_foundry/index.html b/files/ja/learn/server-side/express_nodejs/installing_on_pws_cloud_foundry/index.html new file mode 100644 index 0000000000..f3a62d8b6e --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/installing_on_pws_cloud_foundry/index.html @@ -0,0 +1,242 @@ +--- +title: PWS/Cloud Foundry に LocalLibrary をインストールする +slug: Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry +translation_of: Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry +--- +
{{LearnSidebar}}
+ +

この記事では Pivotal Web Services の PaaS クラウドに地域図書館をインストールする方法の実際的なデモンストレーションを提供します。これは、チュートリアルのパート7で使用されている PaaS クラウドサービスである Heroku のフル機能のオープンソース代替です。PWS/Cloud Foundry は、Heroku (または別の PaaS クラウドサービス) に代わるものを探している場合、または単に何か違うことを試したい場合には、絶対にチェックする価値があります。

+ +

Why PWS?

+ +

Pivotal Web Services is a public instance of the Open Source Cloud Foundry Platform. It is a polyglot platform supporting many different languages including Node.js, Java, PHP, Python, Staticfiles, and Ruby. It has a introductory free trial and is incredibly efficient for running Node applications! As Node and Express are open source projects, there is consistency with working with an open deployment platform such as Cloud Foundry. You can get under the hood and see how an application is hosted.

+ +

There are multiple reasons to use PWS!

+ + + +

How does PWS work?

+ +

PWS runs websites and applications using containers and has been for many years. Cloud Foundry started using a container technology called Warden and is now using a container system called Garden. These are very similar to the popular Docker container and in fact, many installations of Cloud Foundry support deploying Docker containers.

+ +

One of the advantages of using Cloud Foundry is that you do not need to create the container spec, as Cloud Foundry's buildpacks will manufacture them based on the latest components. Apps on Cloud Foundry should follow 12 Factor guidelines as the containers deployed are ephemeral and may be cleaned up at anytime and redeployed somewhere else in the cloud. This ensures that your apps and platform have the latest software. An application can consist of multiple instances where the application is placed into redundant containers that enable high availability of your app. Cloud Foundry will automatically handle all the load balancing between identical instances. This allows you to scale your application for performance and availability

+ +

Since the file system is ephemeral any temporary storage or services should be located elsewhere using backing services. This can be done using marketplace services available on different providers or by bringing your own via User Provided Services.

+ +

What do we cover below?

+ +

This post covers how to modify the LocalLibrary application from the tutorial for deployment on PWS and Cloud Foundry. In doing so, it covers the basics of deploying any node.js application to PWS with the following steps.

+ + + +

So let's get started. You have two options, you can go through the tutorial from the beginning or you can just download the completed project and modify it from there for use on PWS. To do the latter, you can do the following from a terminal:

+ +
git clone https://github.com/mdn/express-locallibrary-tutorial
+ +

You'll then need to follow the preparation steps listed in the Getting your website ready to publish section of Express Tutorial Part 7: Deploying to production, before then following the steps listed below.

+ +
+

Note: This work flow is based on the Mozilla Heroku work flow in the main Express/Node tutorial series for consistency, to help readers compare and contrast. 

+
+ +

Modifying the LocalLibrary for PWS

+ +

Deployment of a Node application on Cloud Foundry takes the following steps. Using the downloaded 'cf' CLI tool on your environment, your source code and supporting metadata files are uploaded to Cloud Foundry which will assemble and package the components of your application. Note that your files need to be located on your system to deploy or as a zip archive somewhere accessible on the internet. We'll assume the former.

+ +

This means, no assumptions about which source control system is used. As long as you have a complete source tree in your local file system you can deploy the app. There are some things you have to make available to ensure the correctly assembly of your Node application. First Cloud Foundry cf CLI will look for the presence of the 'package.json' file to determine the dependencies and download the necessary components. The rules of this assembly are defined in Cloud Foundry's nodejs buildpack. An optional cloud foundry manifest file can specify information about your application such as name, size and start command if non-standard. In addition to deploying the application, the cf CLI tool can also configure services, set environment variables and view logs. That's all the overview you need in order to get started (see Getting Started on Pivotal Web Services for a more comprehensive guide). Let's start making the changes so you'll need to deploy the LocalLibrary application to  PWS.

+ +

Set node version

+ +

The package.json contains everything needed to work out your application dependencies and what file should be launched to start your site. Cloud Foundry and PWS detects the presence of this file, and will use it to provision your app environment. The only useful information missing in our current package.json is the version of node. We can find the version of node we're using for development by entering the command:

+ +
node --version
+# will return version e.g. v6.10.3
+ +

Open package.json with a text editor, and add this information as an engines > node section as shown (using the version number retrieved above).

+ +
{
+  "name": "express-locallibrary-tutorial",
+  "version": "0.0.0",
+  "engines": {
+    "node": "6.10.3"
+  },
+  "private": true,
+  ...
+
+ +

Database configuration

+ +

So far in this tutorial we've used a single database that is hard coded into the app.js file. Normally we'd like to be able to have a different database for production and development, so next we'll modify the LocalLibrary website to get the database URI from the OS environment, and otherwise use our development database that we added manually earlier. Cloud Foundry has a very flexible services model that enables multiple services of the same type to exist in the environment. It stores all services related configurations in a single parseable JSON object called VCAP_SERVICES. A typical VCAP_SERVICES variable looks like this:

+ +
{
+ "VCAP_SERVICES": {
+  "mlab": [
+   {
+    "credentials": {
+     "uri": "mongodb://CloudFoundry_test_dev:somecr8zypassw0rd@dbhost.mlab.com:57971/CloudFoundry_dbname"
+    },
+    "label": "mlab",
+    "name": "node-express-tutorial-mongodb",
+    "plan": "sandbox",
+    "provider": null,
+    "syslog_drain_url": null,
+    "tags": [
+     "Cloud Databases",
+     "Developer Tools",
+     "Web-based",
+     "Data Store",
+    ],
+    "volume_mounts": []
+   }
+  ]
+ }
+}
+
+
+ +

Writing the code to extract and parse this environment variable is not hard, but it doesn't add a lot of value when others have written libraries to do this. In this case, there is a node.js package we can use called cfenv.

+ +

This will download the cfenv module and its dependencies, and modify the package.json file as required. Open app.js and find the block with all the 'requires' that load the modules into your application. In this example look for the line that looks something like this:

+ +
var expressValidator = require('express-validator');
+ +

If you cannot find that exact line, look for the blocks of 'requires' and look for the last one. Add the following text after it:

+ +
var cfenv = require('cfenv');
+ +
    +
  1. +

    To install the package, go to your terminal and make sure you are in the directory where the package.json file for LocalLibrary is. From the command line, type:

    + +
    npm install cfenv
    +
  2. +
  3. +

    Now that you have loaded the module, this next line will instantiate an object that will contain the app and services information required for deployment. Add the following after the line that contains app.use(helmet());

    + +
    // Set up CF environment variables
    +var appEnv = cfenv.getAppEnv();
    +
    + +

    When this line executes, all the Cloud Foundry application environment information will become available to the application in the appEnv object.

    +
  4. +
  5. +

    Now it is time to update the application to use the database configured by the platform. Find the line that sets the mongoDB connection variable. It will look something like this:

    + +
    var mongoDB = process.env.MONGODB_URI || dev_db_url;
    +
  6. +
  7. +

    You will now modify the line with the following code  appEnv.getServiceURL('node-express-tutorial-mongodb') to get the connection string from an environment variable that is being managed by the cfenv  module. If no service has been created and bound it will use your own database URL you created as part of the tutorial instead of the one from the environment. So replace the line above with the following:

    + +
    var mongoDB = appEnv.getServiceURL('node-express-tutorial-mongodb') || dev_db_url;
    +
    +
  8. +
  9. +

    Now run the site locally (see Testing the routes for the relevant commands) and check that the site still behaves as you expect. At this point your app is ready to use with Cloud Foundry and Pivotal Web Services.

    +
  10. +
+ +

Get a Pivotal Web Services account

+ +

To start using Pivotal Web Services you will first need to create an account (skip ahead to Create and upload the website if you've already got an account and have already installed the PWS cf CLI client).

+ + + +

Install the cf CLI client

+ +

The cf CLI client is a software tool for managing and deploying your application. Download and install the PWS cf CLI client by following the instructions on Pivotal Web Services or downloading directly from GIthub. Be sure to download the correct version for your computer. After the client is installed you will be able run commands, for example to get help on the client:

+ +
cf help
+
+ +

We'll now go through the steps to login to PWS using the CLI and deploy — or in Cloud Foundry parlance "push" your app.

+ +

Create and upload the website

+ +

To create the app we navigate to the directory where our modified files are. This is the same directory where the LocalLibrary package.json file is located. First, let's tell the cf CLI which Cloud Foundry instance you want to use. We need to do this, since the cf CLI tool can be used with any standard Cloud Foundry system, so this command indicates which specific Cloud Foundry you are using. Enter the following terminal command now:

+ +
cf api api.run.pivotal.io
+ +

Next login using the following command (enter your email and password when prompted):

+ +
cf login
+Email: enter your email
+Password: enter your password
+ +

We can now push our app to PWS. In the below example. replace 'some-unique-name' with something you can remember that is likely to be unique. If it isn't unique, the system will let you know. The reason this name has to be unique to the PWS system is it is the address we will use to to access your LocalLibrary application. I used mozilla-express-tutorial-xyzzy. You should use something else.

+ +
cf push some-unique-name -m 256MB
+ +

Note the -m flag we added is not required. We just included it so that we only use 256MB of memory to run the app. Node apps typically can run in 128 MB, but we are being safe. If we didn't specify the memory, the CLI would use the default 1 GB of memory, but we want to make sure your trial lasts longer. You should now see a bunch of text on the screen. It will indicate that the CLI is uploading all your files, that it's using the node buildpack, and it will start the app. If we're lucky, the app is now "running" on the site at the URL https://some-unique-name.cfapps.io. Open your browser and run the new website by going to that URL.

+ +
Note: The site will be running using our hardcoded development database at this time. Create some books and other objects, and check out whether the site is behaving as you expect. In the next section we'll set it to use our new database.
+ +

Setting configuration variables

+ +

You will recall from a preceding section that we need to set NODE_ENV to 'production' in order to improve our performance and generate less-verbose error messages.

+ +
    +
  1. +

    Do this by entering the following command:

    + +
    cf set-env some-unique-name NODE_ENV production
    +
    +
  2. +
  3. +

    We should also use a separate database for production. Cloud Foundry can take advantage of a marketplace to create a new service and automatically bind it to your app. Bind means place the service database credentials in the environment variable space of the container running your application for you to access. Enter the following commands:

    + +
    cf create-service mlab sandbox node-express-tutorial-mongodb
    +cf bind-service some-unique-name node-express-tutorial-mongodb
    +
    +
  4. +
  5. +

    You can inspect your configuration variables at any time using the cf env some-unique-name command — try this now:

    + +
    cf env some-unique-name
    +
    +
  6. +
  7. +

    In order for your applications to use the new credentials you will have to restage your application, meaning that it will restart and apply the new environment variables. This can be done using the following — enter this command now:

    + +
    cf restage some-unique-name
    +
    +
  8. +
  9. +

    If you check the home page now it should show zero values for your object counts, as the changes above mean that we're now using a new (empty) database.

    +
  10. +
+ +

Debugging

+ +

The PWS cf client provides a few tools for debugging:

+ +
>cf logs some-unique-name --recent  # Show current logs
+>cf logs some-unique-name # Show current logs and keep updating with any new results
+ +

Summary

+ +

If you followed the above steps, you should have now deployed the LocalLibrary app to PWS. Well done! If the deployment wasn't successful, double check all the steps.

diff --git a/files/ja/learn/server-side/express_nodejs/introduction/index.html b/files/ja/learn/server-side/express_nodejs/introduction/index.html new file mode 100644 index 0000000000..c194cff772 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/introduction/index.html @@ -0,0 +1,528 @@ +--- +title: Express/Node のイントロダクション +slug: Learn/Server-side/Express_Nodejs/Introduction +tags: + - Beginner + - CodingScripting + - Express + - Learn + - Node + - nodejs + - server-side + - サーバーサイド + - 初心者 +translation_of: Learn/Server-side/Express_Nodejs/Introduction +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
+ +

Express の最初の記事では ”Node って何だろう?”、”Express って何だろう?”という疑問に答え、なぜ Express ウェブフレームワークが特別なのかについて概要を説明します。主な特徴、Express アプリケーションの主な基本要素(テスト開発環境についてはここではまだ触れません) を大まかに説明します。

+ + + + + + + + + + + + +
前提条件基本的なコンピューターリテラシー。サーバーサイドのウェブサイトプログラミングの一般的な理解、特にウェブサイトにおけるクライアントとサーバーのやりとりの仕組み。
目標Express の特徴、Node との適合性、提供する機能、Express アプリケーションの主要な基本要素に慣れてください。
+ +

Node の紹介

+ +

Node (正式には Node.js) はオープンソースのクロスプラットフォーム、実行環境で、開発者はあらゆるサーバーサイドのツールやアプリケーションを JavaScript で作成することができます。この実行環境はブラウザーコンテキスト外での使用 (すなわち、コンピューターまたはサーバー OS 上での直接実行) を目的としています。そのため、クライアントサイドではブラウザー固有の JavaScript API が省略され、HTTP やファイルシステムライブラリを含む従来の OS API がサポートされます

+ +

ウェブサーバー開発の観点から Node には多くの利点があります。

+ + + +

Node HTTP パッケージを使用することで、Node.js で簡単な ウェブサーバーを作成できます。

+ +

Hello Node.js

+ +

次の例では、URL http://127.0.0.1:8000/ にあるあらゆる種類の HTTP リクエストを待ち受ける ウェブサーバーを作成します。リクエストが受信されると、スクリプトは "Hello World" という文字列でレスポンスします。すでに Node をインストールしている場合は、次の手順に従ってこの例を試すことができます。

+ +
    +
  1. ターミナルを開きます (Windows ではコマンドラインユーティリティを開きます)。
  2. +
  3. プログラムを保存するフォルダ (たとえば "test-node") を作成し、端末に次のコマンドを入力して移動します。
  4. +
+ +
cd test-node
+ +
    +
  1. 好きなテキストエディタを使って "hello.js" というファイルを作成し、次のコードを貼り付けます。
  2. +
+ +
// HTTPモジュールの読み込み
+var http = require("http");
+
+//  HTTPサーバーを作成し、ポート8000でリクエストを行う
+http.createServer(function(request, response) {
+
+   // HTTP ステータスとコンテントタイプを持つ HTTP ヘッダのレスポンスを設定
+   response.writeHead(200, {'Content-Type': 'text/plain'});
+
+   // レスポンスボディー"Hello World"を送信
+   response.end('Hello World\n');
+}).listen(8000);
+
+//  サーバーにアクセスするための URL を出力
+console.log('Server running at http://127.0.0.1:8000/');
+ +
    +
  1. 上記で作成したフォルダにファイルを保存します。
  2. +
  3. ターミナルに戻り、次のコマンドを入力します。
  4. +
+ +
node "hello.js"
+ +

最後に、ウェブブラウザーで "http://localhost:8000" に移動します。テキスト以外は空の ウェブページの左上に "Hello World" というテキストが表示されます。

+ +

ウェブフレームワーク

+ +

その他の一般的なウェブ開発タスクは、Node 自体では直接サポートされていません。異なる HTTP 動詞 (GET, POST, DELETE など) に特定の処理を追加したい場合、別々の URL パス ("routes") でリクエストを個別に処理したり、静的ファイルを提供したり、テンプレートを使用してレスポンスを動的に作成したり、あなた自身でコードを書く必要があります。そうしない場合はウェブフレームワークを使用して、車輪の再発明を避けることができます。

+ +

Express のイントロダクション

+ +

Express は最も一般的な Node ウェブフレームワークであり、他の多くの一般的な Node ウェブフレームワークの基礎となるライブラリです。それは以下のメカニズムを提供します:

+ + + +

Express 自体はかなりシンプルですが、開発者はほぼすべてのウェブ開発問題に対応する互換性のあるミドルウェアパッケージを作成しています。Cookie、セッション、ユーザーログイン、URL パラメータ、POST データ、セキュリティヘッダーなどを扱うライブラリがあります。Express チームが管理するミドルウェア・パッケージのリストは、Express Middleware (一般的なサード・パーティ・パッケージのリストとともに) にあります。

+ +
+

注: この柔軟性は両刃の剣です。ほぼすべての問題や要件に対応するミドルウェアパッケージがありますが、適切なパッケージを使用して作業することは時には挑戦になることがあります。アプリケーションを構造化する「正しい方法」もなく、インターネット上で見つかる多くの例は最適ではないし、ウェブアプリケーションを開発するために必要なことのほんの一部を示しているだけです。

+
+ +

Node と Express はどこから来たのですか?

+ +

Node は 2009 年に Linux 用に最初にリリースされました。NPM パッケージマネージャは 2010 年にリリースされ、ネイティブ Windows サポートは 2012 年に追加されました。現在の LTS リリースは Node v10.13.0 で、最新のリリースは Node 11.2.0 です。これは、豊かな歴史の小さなスナップショットです。もっと知りたいのであれば、Wikipedia を掘り下げてみてください。

+ +

Express は 2010 年 11 月に最初にリリースされ、現在 API のバージョンが 4.16.3 になっています。現在のリリースの変更点については更新履歴を、詳細な履歴リリースノートについては GitHub を参照してください。

+ +

Node と Express はどれくらい普及していますか?

+ +

ウェブフレームワークの普及は、それが維持されるかどうかの指標であり、ドキュメンテーション、アドオンライブラリ、テクニカルサポートの面でどのようなリソースが利用される可能性が高いかという点で重要です。

+ +

サーバー側のフレームワークの普及率 (Hot Frameworks のようなサイトでは、GitHub プロジェクトの数や各プラットフォームの StackOverflow の質問数などの仕組みを使って人気を評価しようとしています) は、すぐに利用可能で決定的なものではありません。より良い質問は、人気のないプラットフォームの問題を避けるために Node と Express が「人気がある」かどうかです。それらは進化し続けていますか?あなたがそれを必要としたら助けを得ることができますか?あなたが Express を学ぶならば、あなたは職を得る機会がありますか?

+ +

Express を使用している有名企業の数、コードベースに貢献している人の数、および無料と有料の両方でサポートを提供している人の数に基づけば、Express は一般的なフレームワークです。

+ +

Expressは指図をしたがりますか?

+ +

ウェブフレームワークはしばしば自身を「指図をしたがる」または「指図をしない」ものと称します。

+ +

指図をしたがるフレームワークは、特定のタスクを扱うのに「正しい方法」があるという考えを持っています。何であれ正しい方法であれば普通よく理解され細かく文書化されているため、(特定のタイプの問題を解決するような)特定の領域における素早い開発をサポートします。しかしながら、彼らが主眼とする領域の外にある問題を解決するにあたっては柔軟性に劣り、利用できるコンポーネントやアプローチの選択肢が限られたものになりがちです。

+ +

一方、指図をしないフレームワークは、目的の達成のためにコンポーネントをつなぎ合わせる最善の方法や、どのコンポーネントを使うかにさえも、あまり制約を設けません。
+ 開発者は、コンポーネントを自分自身で探す必要があるという手間をかければ、特定のタスクを完了させるのに最適なツールの利用をより容易にします。

+ +

Express は指図をしません。リクエストを処理するチェインの中で、互換性のある好きなミドルウェアを、好きな順番で挿し込むことができます。1 つのファイルまたは複数のファイル、任意のディレクトリ構造を使ってアプリケーションを構成できます。
+ ときに選択肢が多すぎるようにも感じられるでしょう!

+ +

Expressコードはどのように見えますか?

+ +

従来のデータ駆動型ウェブサイトでは、ウェブアプリケーションはウェブブラウザー (または他のクライアント) からの HTTP リクエストを待機します。リクエストが受信されると、アプリケーションは URL パターンと、POST データまたは GET データに含まれる可能性のある関連情報に基づいて、必要なアクションを実行します。必要に応じて、データベースから情報を読み書きしたり、リクエストを満たすために必要な他のタスクを実行することができます。アプリケーションはウェブブラウザーにレスポンスを返し、検索されたデータを HTML テンプレートのプレースホルダに挿入することによってブラウザーが表示する HTML ページを動的に作成することがよくあります。

+ +

Express は特定の HTTP 動詞 (GET, POST, SET など) と URL パターン ("Route") に対してどの関数が呼び出されるかを指定するメソッドと、どのテンプレート ("view") エンジンが使用されるかを指定するメソッドを提供します。テンプレートエンジンを使用するには、レスポンスをレンダリングするためのテンプレートファイルを配置します。Express ミドルウェアを使用して、Cookie、セッション、およびユーザー、POST/GET パラメーターなどのサポートを追加することができます。Node がサポートするデータベース・メカニズムを使用できます (Express はデータベース関連の動作を定義しません)。

+ +

次のセクションでは、Express およびノー​​ド・コードを使用して作業するときに表示される一般的な事項について説明します。

+ +

Helloworld Express

+ +

最初に、標準の Express の Hello World の例を考えてみましょう (これについては、以下の各セクションで説明します)。

+ +
+

Tip: Node と Express がすでにインストールされている場合 (または次の記事のようにインストールする場合) は、このコードを app.js というテキストファイルに保存し、bash コマンドプロンプトで次のように呼び出して実行できます。

+ +

node ./app.js

+
+ +
var express = require('express');
+var app = express();
+
+app.get('/', function(req, res) {
+  res.send('Hello World!');
+});
+
+app.listen(3000, function() {
+  console.log('Example app listening on port 3000!');
+});
+ +

最初の2行は require() で express モジュールをインポートして Express アプリケーションを作成します。このオブジェクトは伝統的に app と呼ばれ、HTTP リクエストのルーティング、ミドルウェアの設定、HTML ビューのレンダリング、テンプレートエンジンの登録、アプリケーションの動作を制御するアプリケーション設定の変更 (環境モード、ルート定義の大文字と小文字の区別など) のためのメソッドがあります。

+ +

コードの中央部分 (app.get で始まる3行) はルート定義を示しています。app.get() メソッドは、サイトルートからの相対パス ('/') を持つ HTTP GET リクエストがあるたびに呼び出されるコールバック関数を指定します。コールバック関数はリクエストとレスポンスオブジェクトを引数として取り、レスポンスに対して単に send() を呼び出して文字列 "Hello World!" を返します。

+ +

最後のブロックは3000番ポートでサーバーを起動し、コンソールにログコメントを出力します。 サーバーが稼働している場合は、ブラウザーの localhost:3000 にアクセスして、レスポンスの例を確認することができます。

+ +

モジュールのインポートと作成

+ +

モジュールは Node の require() 関数を使って他のコードにインポートできる JavaScript ライブラリ/ファイルです。 Express 自体はモジュールです。Express アプリケーションで使用するミドルウェアおよびデータベースライブラリも同様です。

+ +

以下のコードは、例として Express フレームワークを使用して、モジュールを名前でインポートする方法を示しています。 最初に require() 関数を呼び出し、モジュールの名前を文字列 ('express') として指定し、返されたオブジェクトを呼び出して Express アプリケーションを作成します。その後、アプリケーションオブジェクトのプロパティと機能にアクセスできます。

+ +
var express = require('express');
+var app = express();
+ +

独自のモジュールを作成して、同じ方法でインポートすることもできます。

+ +
+

メモ: あなたは自身のモジュールを作成したいと思うでしょう、これはあなたが自身のコードを管理しやすい部品に分けることを可能にします 。ちなみに、モノリシックな単一ファイルのアプリケーションは理解し維持するのが難しいです。モジュールを使用すると、明示的にエクスポートした変数のみがインポートされるため、モジュールを使用すると名前空間を管理するのにも役立ちます。

+
+ +

オブジェクトをモジュールの外部で利用可能にするには、それらを exports オブジェクトの追加プロパティとして公開するだけです。たとえば、以下の square.js モジュールは area() メソッドと perimeter() メソッドをエクスポートしたファイルです。

+ +
exports.area = function(width) { return width * width; };
+exports.perimeter = function(width) { return 4 * width; };
+ +

require() を使ってこのモジュールをインポートし、次に示すようにエクスポートされたメソッドを呼び出すことができます。

+ +
var square = require('./square'); //  require() にはファイル拡張子を除いたファイル名を引数に指定します。
+console.log('The area of a square with a width of 4 is ' + square.area(4));
+ +
+

メモ: モジュールへの絶対パス (または最初に行ったように名前) を指定することもできます。

+
+ +

一度に1つのプロパティを構築するのではなく、1つの割り当てで完全なオブジェクトをエクスポートする場合は、次のように module.exports に割り当てます (これを実行して、エクスポートオブジェクトのルートをコンストラクタまたは他の関数にすることもできます)。

+ +
module.exports = {
+  area: function(width) {
+    return width * width;
+  },
+
+  perimeter: function(width) {
+    return 4 * width;
+  }
+};
+ +
+

Note: あなたは exports を与えられたモジュール内の module.exports へのショートカットとして考えることができます。実際、exports は、モジュールが評価される前に module.exports の値に初期化される単なる変数です。 その値はオブジェクト (この場合は空のオブジェクト) への参照です。これは、exportsmodule.exports によって参照されるのと同じオブジェクトへの参照を保持することを意味します。また、エクスポートに別の値を代入することで、module.exports にバインドされなくなることも意味します。

+
+ +

モジュールの詳細については、モジュール (Node API のドキュメント) を参照してください。

+ +

非同期 API の使用

+ +

JavaScriptコードでは、操作に同期APIよりも非同期APIが頻繁に使用されるため、処理に時間がかかることがあります。 同期APIは、各操作を完了してから次の操作を開始できるAPIです。 たとえば、次のログ関数は同期的で、テキストをコンソールに順番に印刷します(First、Second)。

+ +
console.log('First');
+console.log('Second');
+ +

対照的に、非同期 API は、API が操作を開始してすぐに (操作が完了する前に) 戻るものです。操作が終了すると、API は何かのメカニズムを使用して追加の実行を行います。例えば、次のコードでは最初に setTimeout() メソッドが呼び出されてすぐに返されても、操作が数秒間完了しないため、 "Second, First" が出力されます。

+ +
setTimeout(function() {
+   console.log('First');
+   }, 3000);
+console.log('Second');
+ +

Node はシングルスレッドのイベント駆動型実行環境であるため、ノンブロッキングの非同期 API を使用することは、ブラウザーよりも Node にとってさらに重要です。シングルスレッドとは、サーバーへのすべてのリクエストが (別々のプロセスに分割されるのではなく) 同じスレッドで実行されることを意味します。このモデルは速度とサーバーリソースの点で非常に効率的です。しかし、完了に時間がかかる同期メソッドを呼び出す関数があると、それらは現在のリクエストだけでなく、他のすべてのリクエストがウェブアプリケーションによって処理されることをブロックします。

+ +

非同期 API がアプリケーションに完了したことを通知するにはいくつかの方法があります。最も一般的な方法は、非同期 API を呼び出すときにコールバック関数を登録することです。これは、操作が完了したときにコールバックされます。 これが上記で使用されているアプローチです。

+ +
+

Tip: コールバックを使用することは、順番に実行しなければならない一連の従属非同期操作がある場合、かなり "面倒" になる可能性があります。これは、複数レベルのネストされたコールバックをもたらすためです。この問題は一般に「コールバック地獄」として知られています。この問題は、優れたコーディング方法 ( http://callbackhell.com/ を参照)、async などのモジュールの使用、または Promise などの ES6 機能への移行によっても軽減できます。

+
+ +
+

メモ: Node と Express の一般的な規約は、エラー優先コールバックを使うことです。この規約では、コールバック関数の最初の値はエラー値ですが、後続の引数には成功データが含まれます。 なぜこのアプローチがこのブログで役に立つのかについての良い説明があります:Node.jsの方法 - エラーファーストコールバックについて (fredkschott.com)。

+
+ +

ルートハンドラの作成

+ +

上記の Hello World Express の例では、サイトルート ('/') への HTTP GET リクエストに対して(callback)ルートハンドラ関数を定義しました。

+ +
app.get('/', function(req, res) {
+  res.send('Hello World!');
+});
+ +

コールバック関数はリクエストとレスポンスオブジェクトを引数として取ります。 この場合、メソッドは単にレスポンスに対して send() を呼び出して、文字列 "Hello World!" を返します。リクエスト/レスポンスサイクルを終了するためのレスポンスメソッドは他にも多数あります。たとえば、JSONレスポンスを送信するために res.json() を呼び出し、ファイルを送信するために res.sendFile() を呼び出すことができます。

+ +
+

JavaScript tip: コールバック関数で好きな引数名を使うことができます。 コールバックが呼び出されると、最初の引数は常にリクエストになり、2番目の引数は常にレスポンスになります。 コールバックの本体で作業しているオブジェクトを識別できるようにそれらの名前を付けることは意味があります。

+
+ +

Express アプリケーションオブジェクトには、他のすべての HTTP 動詞のルートハンドラを定義するためのメソッドもあります。これらのメソッドはほとんど同じ方法で使用されます。

+ +

checkout()copy()delete()get()head()lock()merge()mkactivity(), mkcol(), move(), m-search(), notify(), options(), patch(), post(), purge(), put(), report(), search(), subscribe(), trace(), unlock(), unsubscribe().

+ +

app.all() という特別なルーティングメソッドがあります。これはあらゆる HTTP メソッドにレスポンスして呼び出されます。これはすべてのリクエストメソッドの特定のパスにミドルウェア機能をロードするために使用されます。次の例 (Express の資料から) は、使用される HTTP 動詞に関係なく、 /secret へのリクエストに対して実行されるハンドラを示しています(http モジュールでサポートされている場合)。

+ +
app.all('/secret', function(req, res, next) {
+  console.log('Accessing the secret section ...');
+  next(); // 次のハンドラに制御を渡します。
+});
+ +

ルートを使用すると、URL 内の特定のパターンの文字を照合し、URL からいくつかの値を抽出し、それらをパラメータとしてルートハンドラに渡すことができます(パラメータとして渡されるリクエストオブジェクトの属性として)。

+ +

多くの場合、サイトの特定の部分のルートハンドラをまとめて、共通のルートプレフィックスを使用してそれらにアクセスすると便利です (たとえば、Wiki のあるサイトでは、1つのファイルにすべての Wiki 関連ルートがあり、ルートプレフィックス /wiki/ を使用してアクセスすることがあります)。 Expressでは、これは express.Router オブジェクトを使用して実現されます。たとえば、wiki.js という名前のモジュールで Wiki ルートを作成してから、次に示すように Router オブジェクトをエクスポートできます。

+ +
// wiki.js - Wiki ルートモジュール
+
+var express = require('express');
+var router = express.Router();
+
+// ホームページルート
+router.get('/', function(req, res) {
+  res.send('Wiki home page');
+});
+
+// about ページルート
+router.get('/about', function(req, res) {
+  res.send('About this wiki');
+});
+
+module.exports = router;
+ +
+

メモ: Router オブジェクトにルートを追加することは、(前述のように) app オブジェクトにルートを追加するのと同じです。

+
+ +

メインアプリケーションファイルでルーターを使用するには、ルートモジュール (wiki.js) を require() してから、Express アプリケーションで use() を呼び出してミドルウェア処理パスにルーターを追加します。 2つの経路は /wiki//wiki/about/ からアクセス可能になります。

+ +
var wiki = require('./wiki.js');
+// ...
+app.use('/wiki', wiki);
+ +

ルートを扱うことについて、そして特に Router を使うことについてもっとより多くのことがあります。それらについては、リンクされたセクション、ルートとコントローラで説明します。

+ +

ミドルウェアの使用

+ +

ミドルウェアは静的ファイルの提供からエラー処理、HTTP レスポンスの圧縮まで、Express アプリケーションで広く使用されています。ルート関数は HTTP クライアントにレスポンスを返すことで HTTP リクエスト - レスポンスサイクルを終了しますが、ミドルウェア関数は通常、リクエストまたはレスポンスに対して何らかの操作を実行してから、「スタック」内の次の機能を呼び出します。これは、より多くのミドルウェアまたはルートハンドラの場合があります。ミドルウェアが呼び出される順序はアプリ開発者次第です。

+ +
+

Note: ミドルウェアは任意の操作を実行し、任意のコードを実行し、リクエストおよびレスポンスオブジェクトに変更を加えることができ、またリクエスト - レスポンスサイクルを終了することもできます。サイクルが終了しない場合は、next() を呼び出して次のミドルウェア機能に制御を渡す必要があります (そうでない場合、リクエストは中断されたままになります)。

+
+ +

Cookie の操作、セッション、ユーザ認証、リクエスト POST および JSON データへのアクセス、ロギングなどの一般的なウェブ開発タスクを簡素化するために、ほとんどのアプリはサードパーティ製ミドルウェアを使用します。Express チームが管理するミドルウェアパッケージのリストを見つけることができます。(他の人気のあるサードパーティのパッケージも含みます)。他の Express パッケージは NPM パッケージマネージャーで入手できます。

+ +

サードパーティのミドルウェアを使用するには、まず NPM を使用してそれをアプリにインストールする必要があります。たとえば、 morgan という HTTP リクエストロガーミドルウェアをインストールするには、次のようにします。

+ +
$ npm install morgan
+
+ +

次に、Express アプリケーションオブジェクトで use() を呼び出してミドルウェアをスタックに追加できます。

+ +
var express = require('express');
+var logger = require('morgan');
+var app = express();
+app.use(logger('dev'));
+...
+ +
+

メモ: ミドルウェアおよびルーティング機能は、宣言されている順序で呼び出されます。ミドルウェアによっては、順序が重要です (たとえば、セッションミドルウェアが cookie ミドルウェアに依存している場合は、最初に cookie ハンドラを追加する必要があります)。ほとんどの場合、ミドルウェアはルートを設定する前に呼び出されます。そうでないとルートハンドラがミドルウェアによって追加された機能にアクセスすることはできません。

+
+ +

あなたは自身のミドルウェア機能を書くことができ、そうする必要があるでしょう (エラー処理コードを作成するためだけの場合)。ミドルウェア関数とルートハンドラコールバックの唯一の違いは、ミドルウェア関数が次に next 引数を持つことです。ミドルウェア関数はリクエストサイクルを完了させるものではない場合に呼び出されます (ミドルウェア関数が呼び出されるとき、その中には呼び出される next 関数が含まれていなければなりません)。

+ +

ミドルウェアをすべてのレスポンスに適用するのか、特定の HTTP 動詞を含むレスポンス (GETPOST など) に適用するのかに応じて、 app.use() または app.add() のいずれかを使用してミドルウェア機能を処理チェーンに追加できます)。 app.use() を呼び出すときの経路はオプションですが、どちらの場合も同じ経路を指定します。

+ +

以下の例は、ルートの有無に関わらず、両方の方法を使用してミドルウェア関数を追加する方法を示しています。

+ +
var express = require('express');
+var app = express();
+
+// ミドルウェア関数の例
+var a_middleware_function = function(req, res, next) {
+  // ... perform some operations
+  next(); // next() を呼ぶことで Express はチェイン中の次のミドルウェア関数を呼びます。
+
+// すべてのルートと動詞に対して use() で関数を追加します。
+app.use(a_middleware_function);
+
+// 指定ルートに対して use() でミドルウェア関数を追加します。
+app.use('/someroute', a_middleware_function);
+
+// 指定の HTTP 動詞とルートに対してミドルウェア関数を追加します。
+app.get('/', a_middleware_function);
+
+app.listen(3000);
+ +
+

JavaScript Tip: 上記ではミドルウェア関数を別々に宣言してからコールバックとして設定しています。以前のルートハンドラ関数では、使用時にコールバック関数を宣言しました。JavaScript では、どちらの方法も有効です。

+
+ +

Express の資料には、Express ミドルウェアの使用および作成に関するより優れた資料があります。

+ +

静的ファイルの提供

+ +

express.static ミドルウェアを使用して、画像、CSS、JavaScript などの静的ファイルを提供できます (static() は、実際には Express の一部である唯一のミドルウェア関数です)。 たとえば、node を呼び出す場所と同じレベルで、'public' という名前のディレクトリーから画像、CSS ファイル、および JavaScript ファイルを配信するには、次の行を使用します。

+ +
app.use(express.static('public'));
+ +

public ディレクトリー内のファイルはすべて、ベース URL にそのファイル名 (ベースの "public" ディレクトリーに対する相対パス) を追加することによって提供されます。そのため、例えば:

+ +
http://localhost:3000/images/dog.jpg
+http://localhost:3000/css/style.css
+http://localhost:3000/js/app.js
+http://localhost:3000/about.html
+
+ +

static() を複数回呼び出して、複数のディレクトリーを扱うことができます。ファイルが1つのミドルウェア関数で見つからない場合は、そのファイルは後続のミドルウェアに単純に渡されます (ミドルウェアが呼び出される順序は宣言の順序に基づいています)。

+ +
app.use(express.static('public'));
+app.use(express.static('media'));
+ +

ファイルをベース URL に追加するのではなく、静的 URL の仮想プレフィックスを作成することもできます。たとえば、ここではファイルがプレフィックス "/media" でロードされるようにマウントパスを指定します。

+ +
app.use('/media', express.static('public'));
+ +

これで、public ディレクトリーにあるファイルを /media パスプレフィックスから読み込むことができます。

+ +
http://localhost:3000/media/images/dog.jpg
+http://localhost:3000/media/video/cat.mp4
+http://localhost:3000/media/cry.mp3
+
+ +

詳しくは、Expressでの静的ファイルの提供を参照してください。

+ +

エラーの処理

+ +

エラーは、通常の3つの引数ではなく、4つの引数 (err、req、res、next) を持つ1つ以上の特別なミドルウェア関数によって処理されます。例えば:

+ +
app.use(function(err, req, res, next) {
+  console.error(err.stack);
+  res.status(500).send('Something broke!');
+});
+ +

これらは必要なコンテンツを返すことができますが、他のすべての app.use() および呼び出しをルーティングした後に呼び出して、リクエスト処理プロセスの最後のミドルウェアになるようにする必要があります。

+ +

Express にはエラーハンドラが組み込まれています。これは、アプリで発生する可能性がある残りのエラーを処理します。 このデフォルトのエラー処理ミドルウェア関数は、ミドルウェア関数スタックの最後に追加されます。next() にエラーを渡し、それをエラーハンドラで処理しなかった場合、それは組み込みエラーハンドラによって処理されます。エラーはスタックトレースとともにクライアントに書き込まれます。

+ +
+

メモ: スタックトレースは実稼働環境に含まれていません。プロダクションモードで実行するには、環境変数 NODE_ENV を 'production' に設定する必要があります。

+
+ +
+

メモ: HTTP 404 およびその他の "エラー" ステータスコードはエラーとして扱われません。これらを処理したい場合は、ミドルウェア関数を追加して処理することができます。詳しくは FAQ を見てください。

+
+ +

詳しくはエラー処理 (Express ドキュメント) を参照してください。

+ +

データベースの使用

+ +

Express アプリケーションは、Node によってサポートされている任意のデータベースメカニズムを使用できます (Express 自体はデータベース管理のための特定の追加の動作や要件を定義していません)。PostgreSQL、MySQL、Redis、SQLite、MongoDB などを含む多くのオプションがあります。

+ +

これらを使用するには、まず NPM を使用してデータベースドライバをインストールする必要があります。たとえば、一般的な NoSQL MongoDB 用のドライバをインストールするには、次のコマンドを使用します。

+ +
$ npm install mongodb
+
+ +

データベース自体はローカルにインストールすることも、クラウドサーバーにインストールすることもできます。Express コードではドライバが必要で、データベースへの接続から、作成、参照、更新、削除 (CRUD) 操作を実行します。以下の (Express ドキュメントからの) 例は、MongoDB を使ってどのように「哺乳類の」レコードを見つけることができるかを示しています。

+ +
// これは mongodb バージョン 2.2.33 までの古いバージョンで動作します
+var MongoClient = require('mongodb').MongoClient;
+
+MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
+  if (err) throw err;
+
+  db.collection('mammals').find().toArray(function (err, result) {
+    if (err) throw err;
+
+    console.log(result);
+  });
+});
+
+
+// mongodb バージョン 3.0 以上のためのコード
+let MongoClient = require('mongodb').MongoClient;
+MongoClient.connect('mongodb://localhost:27017/animals', function(err, client){
+   if(err) throw err;
+
+   let db = client.db('animals');
+   db.collection('mammals').find().toArray(function(err, result){
+     if(err) throw err;
+     console.log(result);
+     client.close();
+   });
+});
+ +

もう1つの一般的な方法は、Object Relational Mapper ( "ORM") を介して間接的にデータベースにアクセスすることです。このアプローチではデータを「オブジェクト」または「モデル」として定義し、ORM はそれらを基礎となるデータベース形式にマッピングします。このアプローチには、開発者としてデータベースのセマンティクスではなく JavaScript オブジェクトの観点から考え続けることができ、受信データの検証とチェックを実行するための明らかな場所があるという利点があります。データベースについての詳細は、後の記事で説明します。

+ +

詳しくはデータベース統合 (Express ドキュメント) を参照してください。

+ +

データのレンダリング (ビュー)

+ +

テンプレートエンジン (Express では「ビューエンジン」と呼ばれます) を使用すると、ページの生成時に埋められるデータのプレースホルダを使用して、テンプレート内の出力ドキュメントの構造を指定できます。テンプレートは HTML の作成によく使用されますが、他の種類の文書も作成できます。 Express はいくつかのテンプレートエンジンをサポートしています。ここでは、より人気の高いエンジンの便利な比較ができます。JavaScript テンプレートエンジンの比較:Jade、Moustache、Dustなど

+ +

次に示すように、アプリケーション設定コードで、使用するテンプレートエンジンと、Express が 'ビュー' および 'ビューエンジン' 設定を使用してテンプレートを探す場所を設定します (テンプレートライブラリを含むパッケージもインストールする必要があります!)

+ +
var express = require('express');
+var app = express();
+
+// ('views') テンプレートを含むディレクトリーを設定
+app.set('views', path.join(__dirname, 'views'));
+
+// 利用するビューエンジン、この場合は 'some_template_engine_name' を設定
+app.set('view engine', 'some_template_engine_name');
+ +

テンプレートの外観は使用するエンジンによって異なります。"title" および "message" という名前のデータ変数のプレースホルダを含む "index.<テンプレート拡張子>" という名前のテンプレートファイルがあると仮定すると、ルートハンドラ関数で Response.render() を呼び出して HTML レスポンスを作成し、送信することになります。

+ +
app.get('/', function(req, res) {
+  res.render('index', { title: 'About dogs', message: 'Dogs rock!' });
+});
+ +

詳しくは Express でのテンプレートエンジンの使用 (Express ドキュメント) を参照してください。

+ +

ファイル構造

+ +

Express は、構造や使用するコンポーネントに関しては何も想定していません。ルート、ビュー、静的ファイル、およびその他のアプリケーション固有のロジックは、任意のディレクトリー構造を持つ任意の数のファイルに存在できます。 Express アプリケーション全体を1つのファイルにまとめることは完全に可能ですが、通常は機能 (アカウント管理、ブログ、ディスカッション掲示板など) およびアーキテクチャ上の問題のドメイン (MVC アーキテクチャを使用している場合は、モデル、ビュー、コントローラーなど) に基づいてアプリケーションをファイルに分割します。

+ +

後のトピックでは、ウェブアプリケーションを作成するために簡単に拡張できるモジュール式のアプリケーションスケルトンを作成する Express Application Generator を使用します。

+ + + +

まとめ

+ +

おめでとうございます、Express/Node の旅の最初のステップを完了しました。これで Express と Node の主な利点と、Express アプリケーションの主要部分がどのように見えるか (ルート、ミドルウェア、エラー処理、およびテンプレートコード) をおおまかに理解できています。また、Express は指図しないフレームワークであるため、これらの部分をどのようにまとめるかやどのライブラリを使用するかは、ほとんどあなた次第です。

+ +

もちろん、Express は意図的に非常に軽量なウェブアプリケーションフレームワークであるため、その利点と可能性の多くはサードパーティのライブラリと機能からもたらされています。以下の記事でそれらをより詳しく見ていきます。次回の記事では、Node 開発環境のセットアップについて見ていきます。そうすれば、いくつかの Express コードが実際に動作しているところを見始めることができます。

+ +

関連情報

+ + + +
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/mongoose/index.html b/files/ja/learn/server-side/express_nodejs/mongoose/index.html new file mode 100644 index 0000000000..e6eecb4496 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/mongoose/index.html @@ -0,0 +1,799 @@ +--- +title: 'Express チュートリアル Part 3: データベースの使用 (Mongoose を使用)' +slug: Learn/Server-side/Express_Nodejs/mongoose +translation_of: Learn/Server-side/Express_Nodejs/mongoose +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}
+ +

この記事ではデータベースと、それらを Node/Express アプリケーションで使用する方法について簡単に紹介します。続いて、Mongoose を使用して地域図書館 Web サイトへのデータベースアクセスを提供する方法を説明します。 オブジェクトスキーマとモデルの宣言方法、主なフィールドタイプ、および基本的な検証について説明します。また、モデルデータにアクセスするための主な方法についても簡単に説明します。

+ + + + + + + + + + + + +
前提条件:Express チュートリアル Part 2: スケルトン Web サイトの作成
目標:Mongoose を使用して独自のモデルを設計および作成できるようになる。
+ +

概要

+ +

図書館職員は本と借り手についての情報を保存するためにローカルライブラリ Web サイトを使いますが、図書館員は本をブラウズして検索し、利用可能なコピーがあるかどうかを調べ、そしてそれらを予約または借りるために使います。情報を効率的に保存および取得するために、データベースに保存します。

+ +

Express アプリケーションはさまざまなデータベースを使用できます。作成、読み取り、更新、削除 (CRUD) 操作を実行するために使用できるいくつかのアプローチがあります。 このチュートリアルではいくつかの利用可能なオプションの簡単な概要を説明し、次に選択された特定のメカニズムを詳細に表示します。

+ +

どのデータベースを使用できますか?

+ +

Express アプリは Node でサポートされている任意のデータベースを使用できます (Express 自体はデータベース管理のための特定の追加の動作や要件を定義していません)。 PostgreSQL、MySQL、Redis、SQLite、MongoDB など、多くの一般的なオプションがあります。

+ +

データベースを選択するときは、生産性/学習時間の曲線、パフォーマンス、複製/バックアップの容易さ、コスト、コミュニティサポートなどのことを考慮する必要があります。「最高の」データベースは1つもありませんが、ほとんどの一般的なソリューションは、ローカルライブラリのような中小規模のサイトでは十分条件を満たしているはずです。

+ +

オプションの詳細については、データベース統合 (Express ドキュメント) を参照してください。

+ +

データベースを利用するための最良の方法は何ですか?

+ +

データベースにインタラクティブにアプローチするには2つの方法があります。

+ + + +

SQL、またはデータベースでサポートされているクエリ言語を使用すると、最高のパフォーマンスが得られます。ODM は、変換コードを使用してオブジェクトとデータベース形式の間のマッピングを行うため、処理が遅くなることが多く、最も効率的なデータベースクエリが使用されない可能性があります (これは、ODM がさまざまなデータベースバックエンドをサポートしている場合に特に当てはまります。サポートされているデータベース機能に関して、さらに妥協する必要があります)。

+ +

ORM を使用する利点は、プログラマがデータベースのセマンティクスではなく JavaScript オブジェクトの観点から考え続けることができることです。これは、同じデータベースまたは異なる Web サイトで異なるデータベースを扱う必要がある場合に特に当てはまります。またデータの検証とチェックを実行するための明らかな場所を提供します。

+ +
+

Tip:  ODM/ORM を使用すると、多くの場合、開発と保守のコストが削減されます。ネイティブのクエリ言語に精通しているかパフォーマンスが最優先であるのでなければ、ODM の使用を積極的に検討するべきです。

+
+ +

どの ORM/ODM を使うべきですか?

+ +

NPM パッケージマネージャのサイトには、多数の ODM/ORM ソリューションがあります (サブセットの odm タグおよび orm タグを調べてください)。

+ +

執筆時点で一般的だったいくつかの解決策は、次のとおりです。

+ + + +

原則として、解決策を選択する際には、提供されている機能と "コミュニティ活動" (ダウンロード、コントリビュート、バグレポート、ドキュメントの品質など) の両方を考慮する必要があります。この記事を書いている時点では、Mongoose は最も人気のある ODM であり、データベースに MongoDB を使用している場合は妥当な選択です。

+ +

ローカルライブラリに Mongoose と MongoDB を使用する

+ +

ローカルライブラリの例 (およびこのトピックの残りの部分) では、Mongoose ODM を使用してライブラリデータにアクセスします。Mongoose は、ドキュメント指向のデータモデルを使用するオープンソースの NoSQL データベースである MongoDB のフロントエンドとして機能します。MongoDB データベースの "ドキュメント" の "コレクション" は、リレーショナルデータベースの "行" の "テーブル" に似ています

+ +

この ODM とデータベースの組み合わせは、Node コミュニティで非常に人気があります。これは、ドキュメントの保存とクエリのシステムが JSON に非常に似ているため、JavaScript 開発者にはよく知られているためです。

+ +
+

Tip: Mongoose を使用するために MongoDB を知っている必要はありませんが、Mongoose のドキュメントの一部は、MongoDB に慣れている方が使いやすく理解しやすいものです。

+
+ +

このチュートリアルの残りの部分では、ローカルライブラリ Web サイトの例の Mongoose スキーマとモデルを定義してアクセスする方法を示します。

+ +

ローカルライブラリモデルの設計

+ +

いきなりモデルのコーディングを始める前に、格納する必要があるデータと、さまざまなオブジェクト間の関係について検討することをお勧めします。

+ +

書籍に関する情報 (タイトル、概要、著者、ジャンル、ISBN) を保存する必要があること、および複数のコピーが利用可能であること (グローバルに一意の ID、利用状況など) があることを知っています。著者の名前だけではなく、著者に関するより多くの情報を格納する必要があるかもしれません。また、同じ名前または類似の名前を持つ著者が複数いる可能性があります。書籍のタイトル、著者、ジャンル、およびカテゴリに基づいて情報を並べ替えることができるようにします。

+ +

モデルを設計するときは、すべての "オブジェクト" (関連情報のグループ) ごとに別々のモデルを用意するのが合理的です。 この場合、明らかなオブジェクトは本、本のインスタンス、および作者です。

+ +

Web サイト自体に選択肢をハードコーディングするのではなく、モデルを使用して選択肢の選択肢 (たとえば選択肢のドロップダウンリストなど) を表すこともできます - すべてのオプションが事前にわかっていない場合や変更される可能性がある場合は、これをお勧めします。このタイプのモデルの明らかな候補は本のジャンルです (例:サイエンスフィクション、フランス詩など)。

+ +

モデルとフィールドを決めたら、それらの関係について考える必要があります。

+ +

そのことを念頭に置いて、以下の UML 関連図は、この場合に定義するモデルを (ボックスとして) 示しています。上記で説明したように、本のモデル (本の一般的な詳細)、本のインスタンス (システムで利用可能な本の特定の物理コピーのステータス)、および作成者のモデルを作成しました。また、値を動的に作成できるように、ジャンルのモデルを用意することにしました。BookInstance:status のモデルを使用しないことにしました - 許容値は変更しないと考えられるので、許容値をハードコードします。各ボックス内には、モデル名、フィールド名と型、そしてメソッドとその戻り型が表示されます。

+ +

この図には、モデル間の関係 (それらの多重度も含む) も示されています。多重度は、関係内に存在する可能性がある各モデルの番号 (最大および最小) を示す図上の番号です。たとえば、ボックス間の接続線は、BookGenre が関連していることを示しています。Book モデルに近い数字は、ジャンルに0個以上の Book がある必要があることを示しており、線のもう一方の端にあるGenre の隣の数字は、本に0個以上の関連するGenreがあることを示しています。

+ +
+

メモ: 下記の Mongoose 入門書で説明されているように、1つのモデルだけで documents/models 間の関係を定義するフィールドがあるほうがよいでしょう (他のモデルで関連する _id を検索することによって逆の関係を見つけることができます)。以下では、Book スキーマの Book/Genre と Book/Author の関係、および BookInstance スキーマの Book/BookInstance の関係を定義します。この選択は多少恣意的でした - 他のスキーマでも同じようにフィールドを持つことができました。

+
+ +

Mongoose Library Model  with correct cardinality

+ +
+

メモ: 次のセクションでは、モデルの定義方法と使用方法を説明する基本的な入門書を提供します。お読みになったところで、上の図の各モデルをどのように構築するかを検討してください。

+
+ +

Mongoose 入門書

+ +

このセクションでは、Mongoose を MongoDB データベースに接続する方法、スキーマとモデルを定義する方法、そして基本的なクエリを作成する方法の概要を説明します。

+ +
+

メモ: この入門書は、npm の Mongoose クイックスタート公式ドキュメントに "大きく影響を受けています"。

+
+ +

Mongoose と MongoDB のインストール

+ +

Mongoose は他の依存関係と同じようにあなたのプロジェクト (package.json) にインストールされます。つまり NPM を使用します。インストールするには、プロジェクトフォルダ内で次のコマンドを使用します。

+ +
npm install mongoose
+
+ +

Mongoose をインストールすると、MongoDB データベースドライバを含むすべての依存関係が追加されますが、MongoDB 自体はインストールされません。 MongoDB サーバをインストールする場合は、さまざまな OS 用のインストーラをここからダウンロードしてローカルにインストールできます。クラウドベースの MongoDB インスタンスを使用することもできます。

+ +
+

メモ: このチュートリアルでは、mLab クラウドベースのDatabase as a Service サンドボックス層として使用してデータベースを提供します。これは開発に適しており、オペレーティングシステムの "インストール" に依存しないため (database-as-a-service も本番データベースに使用することができる1つのアプローチです)、チュートリアルに適しています。

+
+ +

MongoDB への接続

+ +

MongooseはMongoDBデータベースへの接続を必要とします。以下のように、require() して mongoose.connect() でローカルにホストされているデータベースに接続することができます。

+ +
//Import the mongoose module
+var mongoose = require('mongoose');
+
+//Set up default mongoose connection
+var mongoDB = 'mongodb://127.0.0.1/my_database';
+mongoose.connect(mongoDB);
+// Get Mongoose to use the global promise library
+mongoose.Promise = global.Promise;
+//Get the default connection
+var db = mongoose.connection;
+
+//Bind connection to error event (to get notification of connection errors)
+db.on('error', console.error.bind(console, 'MongoDB connection error:'));
+ +

デフォルトの Connection オブジェクトは mongoose.connection で取得できます。接続されると、open イベントが Connection インスタンスで発生します。

+ +
+

Tip: 追加のコネクションを作成する必要がある場合は、mongoose.createConnection() を使用できます。 これは connect() と同じ形式のデータベース URI (ホスト、データベース、ポート、オプションなど) を取り、Connection オブジェクトを返します。

+
+ +

モデルの定義と作成

+ +

モデルは Schema インターフェイスを使用して定義されます。スキーマを使用すると、各ドキュメントに格納されているフィールドとその検証要件およびデフォルト値を定義できます。さらに、データ型や他のフィールドと同じように使用できるが実際にはデータベースに格納されていない仮想プロパティも扱いやすいように、静的メソッドおよびインスタンスヘルパーメソッドを定義できます。(もう少し後で説明します)。

+ +

その後、スキーマは mongoose.model() メソッドを使用してモデルに "コンパイル" されます。モデルを作成したら、それを使用して特定のタイプのオブジェクトを検索、作成、更新、および削除できます。

+ +
+

メモ: 各モデルは MongoDB データベース内のドキュメントのコレクションにマップされます。ドキュメントはモデル Schema で定義されたフィールド/スキーマタイプを含みます。

+
+ +

スキーマの定義

+ +

以下のコードは、単純なスキーマを定義する方法を示しています。最初に mongoose を require() し、次に Schema コンストラクタを使用して新しいスキーマインスタンスを作成し、コンストラクタの object パラメータで内部のさまざまなフィールドを定義します。

+ +
//Require Mongoose
+var mongoose = require('mongoose');
+
+//Define a schema
+var Schema = mongoose.Schema;
+
+var SomeModelSchema = new Schema({
+  a_string: String,
+  a_date: Date
+});
+
+ +

上記の場合、文字列と日付の2つのフィールドしかありません。次のセクションでは、他のフィールドタイプ、検証、その他の方法について説明します。

+ +

モデルを作成する

+ +

モデルは、mongoose.model() メソッドを使用してスキーマから作成されます。

+ +
// Define schema
+var Schema = mongoose.Schema;
+
+var SomeModelSchema = new Schema({
+  a_string: String,
+  a_date: Date
+});
+
+// Compile model from schema
+var SomeModel = mongoose.model('SomeModel', SomeModelSchema );
+ +

最初の引数はあなたのモデル用に作成されるコレクションの単数形の名前です (Mongoose は上記の SomeModel モデル用のデータベースコレクションを作成します)、そして2番目の引数はモデルの作成に使用したいスキーマです。

+ +
+

メモ: モデルクラスを定義したら、それらを使用してレコードを作成、更新、または削除し、クエリを実行してすべてのレコードまたは特定のレコードのサブセットを取得できます。これを行う方法をモデルの使用セクションで、そしてビューを作成するときに示します。

+
+ +

スキーマ型 (フィールド)

+ +

スキーマには任意の数のフィールドを含めることができます。各フィールドは MongoDB に格納されているドキュメント内のフィールドを表します。一般的なフィールド型の多くとその宣言方法を示すスキーマの例を以下に示します。

+ +
var schema = new Schema(
+{
+  name: String,
+  binary: Buffer,
+  living: Boolean,
+  updated: { type: Date, default: Date.now() },
+  age: { type: Number, min: 18, max: 65, required: true },
+  mixed: Schema.Types.Mixed,
+  _someId: Schema.Types.ObjectId,
+  array: [],
+  ofString: [String], // 他の型でも配列にすることができます。
+  nested: { stuff: { type: String, lowercase: true, trim: true } }
+})
+ +

ほとんどのスキーム型("type:"やフィールド名で記述されるもの)はその名のとおりです。例外は以下のようなものがあります:

+ + + +

このコードはフィールドを宣言する2つのやり方も示しています:

+ + + +

もっとオプションについて見たいならSchemaTypes (Mongoose docs)を見てみてください.

+ +

バリデーション

+ +

Mongooseはビルドインもしくはカスマイズしたバリデータや同期的もしくは非同期的なバリデータを提供しています。 バリデータはすべての場合において、許容範囲または値と検証失敗のエラーメッセージの両方を指定できます。

+ +

ビルドインのバリデータには:

+ + + +

以下の例(Mongooseドキュメントから少し変更しています)では、いくつかのバリデータタイプとエラーメッセージを指定する方法を示しています:

+ +

+    var breakfastSchema = new Schema({
+      eggs: {
+        type: Number,
+        min: [6, 'Too few eggs'],
+        max: 12,
+        required: [true, 'Why no eggs?']
+      },
+      drink: {
+        type: String,
+        enum: ['Coffee', 'Tea', 'Water',]
+      }
+    });
+
+ +

詳しくは Validation (Mongoose docs) を見てみてください。

+ +

Virtual properties

+ +

Virtual properties are document properties that you can get and set but that do not get persisted to MongoDB. The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage. The example in the documentation constructs (and deconstructs) a full name virtual property from a first and last name field, which is easier and cleaner than constructing a full name every time one is used in a template.

+ +
+

Note: We will use a virtual property in the library to define a unique URL for each model record using a path and the record's _id value.

+
+ +

For more information see Virtuals (Mongoose documentation).

+ +

Methods and query helpers

+ +

A schema can also have instance methods, static methods, and query helpers. The instance and static methods are similar, but with the obvious difference that an instance method is associated with a particular record and has access to the current object. Query helpers allow you to extend mongoose's chainable query builder API (for example, allowing you to add a query "byName" in addition to the find(), findOne() and findById() methods).

+ +

Using models

+ +

Once you've created a schema you can use it to create models. The model represents a collection of documents in the database that you can search, while the model's instances represent individual documents that you can save and retrieve.

+ +

We provide a brief overview below. For more information see: Models (Mongoose docs).

+ +

Creating and modifying documents

+ +

To create a record you can define an instance of the model and then call save(). The examples below assume SomeModel is a model (with a single field "name") that we have created from our schema.

+ +
// Create an instance of model SomeModel
+var awesome_instance = new SomeModel({ name: 'awesome' });
+
+// Save the new model instance, passing a callback
+awesome_instance.save(function (err) {
+  if (err) return handleError(err);
+  // saved!
+});
+
+ +

Creation of records (along with updates, deletes, and queries) are asynchronous operations — you supply a callback that is called when the operation completes. The API uses the error-first argument convention, so the first argument for the callback will always be an error value (or null). If the API returns some result, this will be provided as the second argument.

+ +

You can also use create() to define the model instance at the same time as you save it. The callback will return an error for the first argument and the newly-created model instance for the second argument.

+ +
SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
+  if (err) return handleError(err);
+  // saved!
+});
+ +

Every model has an associated connection (this will be the default connection when you use mongoose.model()). You create a new connection and call .model() on it to create the documents on a different database.

+ +

You can access the fields in this new record using the dot syntax, and change the values. You have to call save() or update() to store modified values back to the database.

+ +
// Access model field values using dot notation
+console.log(awesome_instance.name); //should log 'also_awesome'
+
+// Change record by modifying the fields, then calling save().
+awesome_instance.name="New cool name";
+awesome_instance.save(function (err) {
+   if (err) return handleError(err); // saved!
+   });
+
+ +

Searching for records

+ +

You can search for records using query methods, specifying the query conditions as a JSON document. The code fragment below shows how you might find all athletes in a database that play tennis, returning just the fields for athlete name and age. Here we just specify one matching field (sport) but you can add more criteria, specify regular expression criteria, or remove the conditions altogether to return all athletes.

+ +
var Athlete = mongoose.model('Athlete', yourSchema);
+
+// find all athletes who play tennis, selecting the 'name' and 'age' fields
+Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) {
+  if (err) return handleError(err);
+  // 'athletes' contains the list of athletes that match the criteria.
+})
+ +

If you specify a callback, as shown above, the query will execute immediately. The callback will be invoked when the search completes.

+ +
+

Note: All callbacks in Mongoose use the pattern callback(error, result). If an error occurs executing the query, the error parameter will contain an error document and result will be null. If the query is successful, the error parameter will be null, and the result will be populated with the results of the query.

+
+ +

If you don't specify a callback then the API will return a variable of type Query. You can use this query object to build up your query and then execute it (with a callback) later using the exec() method.

+ +
// find all athletes that play tennis
+var query = Athlete.find({ 'sport': 'Tennis' });
+
+// selecting the 'name' and 'age' fields
+query.select('name age');
+
+// limit our results to 5 items
+query.limit(5);
+
+// sort by age
+query.sort({ age: -1 });
+
+// execute the query at a later time
+query.exec(function (err, athletes) {
+  if (err) return handleError(err);
+  // athletes contains an ordered list of 5 athletes who play Tennis
+})
+ +

Above we've defined the query conditions in the find() method. We can also do this using a where() function, and we can chain all the parts of our query together using the dot operator (.) rather than adding them separately. The code fragment below is the same as our query above, with an additional condition for the age.

+ +
Athlete.
+  find().
+  where('sport').equals('Tennis').
+  where('age').gt(17).lt(50).  //Additional where query
+  limit(5).
+  sort({ age: -1 }).
+  select('name age').
+  exec(callback); // where callback is the name of our callback function.
+ +

The find() method gets all matching records, but often you just want to get one match. The following methods query for a single record:

+ + + +
+

Note: There is also a count() method that you can use to get the number of items that match conditions. This is useful if you want to perform a count without actually fetching the records.

+
+ +

There is a lot more you can do with queries. For more information see: Queries (Mongoose docs).

+ + + +

You can create references from one document/model instance to another using the ObjectId schema field, or from one document to many using an array of ObjectIds. The field stores the id of the related model. If you need the actual content of the associated document, you can use the populate() method in a query to replace the id with the actual data.

+ +

For example, the following schema defines authors and stories. Each author can have multiple stories, which we represent as an array of ObjectId. Each story can have a single author. The "ref" (highlighted in bold below) tells the schema which model can be assigned to this field.

+ +
var mongoose = require('mongoose')
+  , Schema = mongoose.Schema
+
+var authorSchema = Schema({
+  name    : String,
+  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
+});
+
+var storySchema = Schema({
+  author : { type: Schema.Types.ObjectId, ref: 'Author' },
+  title    : String
+});
+
+var Story  = mongoose.model('Story', storySchema);
+var Author = mongoose.model('Author', authorSchema);
+ +

We can save our references to the related document by assigning the _id value. Below we create an author, then a story, and assign the author id to our stories author field.

+ +
var bob = new Author({ name: 'Bob Smith' });
+
+bob.save(function (err) {
+  if (err) return handleError(err);
+
+  //Bob now exists, so lets create a story
+  var story = new Story({
+    title: "Bob goes sledding",
+    author: bob._id    // assign the _id from the our author Bob. This ID is created by default!
+  });
+
+  story.save(function (err) {
+    if (err) return handleError(err);
+    // Bob now has his story
+  });
+});
+ +

Our story document now has an author referenced by the author document's ID. In order to get the author information in the story results we use populate(), as shown below.

+ +
Story
+.findOne({ title: 'Bob goes sledding' })
+.populate('author') //This populates the author id with actual author information!
+.exec(function (err, story) {
+  if (err) return handleError(err);
+  console.log('The author is %s', story.author.name);
+  // prints "The author is Bob Smith"
+});
+ +
+

Note: Astute readers will have noted that we added an author to our story, but we didn't do anything to add our story to our author's stories array. How then can we get all stories by a particular author? One way would be to add our author to the stories array, but this would result in us having two places where the information relating authors and stories needs to be maintained.

+ +

A better way is to get the _id of our author, then use find() to search for this in the author field across all stories.

+ +
Story
+.find({ author : bob._id })
+.exec(function (err, stories) {
+  if (err) return handleError(err);
+  // returns all stories that have Bob's id as their author.
+});
+
+
+ +

This is almost everything you need to know about working with related items for this tutorial. For more detailed information see Population (Mongoose docs).

+ +

One schema/model per file

+ +

While you can create schemas and models using any file structure you like, we highly recommend defining each model schema in its own module (file), exporting the method to create the model. This is shown below:

+ +
// File: ./models/somemodel.js
+
+//Require Mongoose
+var mongoose = require('mongoose');
+
+//Define a schema
+var Schema = mongoose.Schema;
+
+var SomeModelSchema = new Schema({
+  a_string          : String,
+  a_date            : Date,
+});
+
+//Export function to create "SomeModel" model class
+module.exports = mongoose.model('SomeModel', SomeModelSchema );
+ +

You can then require and use the model immediately in other files. Below we show how you might use it to get all instances of the model.

+ +
//Create a SomeModel model just by requiring the module
+var SomeModel = require('../models/somemodel')
+
+// Use the SomeModel object (model) to find all SomeModel records
+SomeModel.find(callback_function);
+ +

Setting up the MongoDB database

+ +

Now that we understand something of what Mongoose can do and how we want to design our models, it's time to start work on the LocalLibrary website. The very first thing we want to do is set up a MongoDb database that we can use to store our library data.

+ +

For this tutorial, we're going to use mLab's free cloud-hosted "sandbox" database. This database tier is not considered suitable for production websites because it has no redundancy, but it is great for development and prototyping. We're using it here because it is free and easy to set up, and because mLab is a popular database as a service vendor that you might reasonably choose for your production database (other popular choices at the time of writing include Compose, ScaleGrid and MongoDB Atlas).

+ +
+

Note: If you prefer you can set up a MongoDb database locally by downloading and installing the appropriate binaries for your system. The rest of the instructions in this article would be similar, except for the database URL you would specify when connecting.

+
+ +

You will first need to create an account with mLab (this is free, and just requires that you enter basic contact details and acknowledge their terms of service). 

+ +

After logging in, you'll be taken to the home screen:

+ +
    +
  1. Click Create New in the MongoDB Deployments section.
  2. +
  3. This will open the Cloud Provider Selection screen.
    + MLab - screen for new deployment
    + +
      +
    • Select the SANDBOX (Free) plan from the Plan Type section. 
    • +
    • Select any provider from the Cloud Provider section. Different providers offer different regions (displayed below the selected plan type).
    • +
    • Click the Continue button.
    • +
    +
  4. +
  5. This will open the Select Region screen. +

    Select new region screen

    + +
      +
    • +

      Select the region closest to you and then Continue.

      +
    • +
    +
  6. +
  7. +

    This will open the Final Details screen.
    + New deployment database name

    + +
      +
    • +

      Enter the name for the new database as local_library and then select Continue.

      +
    • +
    +
  8. +
  9. +

    This will open the Order Confirmation screen.
    + Order confirmation screen

    + +
      +
    • +

      Click Submit Order to create the database.

      +
    • +
    +
  10. +
  11. +

    You will be returned to the home screen. Click on the new database you just created to open its details screen. As you can see the database has no collections (data).
    + mLab - Database details screen
    +  
    + The URL that you need to use to access your database is displayed on the form above (shown for this database circled above). In order to use this you need to create a database user that you can specify in the URL.

    +
  12. +
  13. Click the Users tab and select the Add database user button.
  14. +
  15. Enter a username and password (twice), and then press Create. Do not select Make read only.
    +
  16. +
+ +

You have now created the database, and have an URL (with username and password) that can be used to access it. This will look something like: mongodb://your_user_namer:your_password@ds119748.mlab.com:19748/local_library.

+ +

Install Mongoose

+ +

Open a command prompt and navigate to the directory where you created your skeleton Local Library website. Enter the following command to install Mongoose (and its dependencies) and add it to your package.json file, unless you have already done so when reading the Mongoose Primer above.

+ +
npm install mongoose
+
+ +

Connect to MongoDB

+ +

Open /app.js (in the root of your project) and copy the following text below where you declare the Express application object (after the line var app = express();). Replace the database url string ('insert_your_database_url_here') with the location URL representing your own database (i.e. using the information from mLab).

+ +
//Set up mongoose connection
+var mongoose = require('mongoose');
+var mongoDB = 'insert_your_database_url_here';
+mongoose.connect(mongoDB);
+mongoose.Promise = global.Promise;
+var db = mongoose.connection;
+db.on('error', console.error.bind(console, 'MongoDB connection error:'));
+ +

As discussed in the Mongoose primer above, this code creates the default connection to the database and binds to the error event (so that errors will be printed to the console). 

+ +

Defining the LocalLibrary Schema

+ +

We will define a separate module for each model, as discussed above. Start by creating a folder for our models in the project root (/models) and then create separate files for each of the models:

+ +
/express-locallibrary-tutorial  //the project root
+  /models
+    author.js
+    book.js
+    bookinstance.js
+    genre.js
+
+ +

Author model

+ +

Copy the Author schema code shown below and paste it into your ./models/author.js file. The scheme defines an author has having String SchemaTypes for the first and family names, that are required and have a maximum of 100 characters, and Date fields for the date of birth and death.

+ +
var mongoose = require('mongoose');
+
+var Schema = mongoose.Schema;
+
+var AuthorSchema = new Schema(
+  {
+    first_name: {type: String, required: true, max: 100},
+    family_name: {type: String, required: true, max: 100},
+    date_of_birth: {type: Date},
+    date_of_death: {type: Date},
+  }
+);
+
+// Virtual for author's full name
+AuthorSchema
+.virtual('name')
+.get(function () {
+  return this.family_name + ', ' + this.first_name;
+});
+
+// Virtual for author's lifespan
+AuthorSchema
+.virtual('lifespan')
+.get(function () {
+  return (this.date_of_death.getYear() - this.date_of_birth.getYear()).toString();
+});
+
+// Virtual for author's URL
+AuthorSchema
+.virtual('url')
+.get(function () {
+  return '/catalog/author/' + this._id;
+});
+
+//Export model
+module.exports = mongoose.model('Author', AuthorSchema);
+
+
+ +

We've also declared a virtual for the AuthorSchema named "url" that returns the absolute URL required to get a particular instance of the model — we'll use the property in our templates whenever we need to get a link to a particular author.

+ +
+

Note: Declaring our URLs as a virtual in the schema is a good idea because then the URL for an item only ever needs to be changed in one place.
+ At this point, a link using this URL wouldn't work, because we haven't got any routes handling code for individual model instances. We'll set those up in a later article!

+
+ +

At the end of the module, we export the model.

+ +

Book model

+ +

Copy the Book schema code shown below and paste it into your ./models/book.js file. Most of this is similar to the author model — we've declared a schema with a number of string fields and a virtual for getting the URL of specific book records, and we've exported the model.

+ +
var mongoose = require('mongoose');
+
+var Schema = mongoose.Schema;
+
+var BookSchema = new Schema(
+  {
+    title: {type: String, required: true},
+    author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},
+    summary: {type: String, required: true},
+    isbn: {type: String, required: true},
+    genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]
+  }
+);
+
+// Virtual for book's URL
+BookSchema
+.virtual('url')
+.get(function () {
+  return '/catalog/book/' + this._id;
+});
+
+//Export model
+module.exports = mongoose.model('Book', BookSchema);
+
+ +

The main difference here is that we've created two references to other models:

+ + + +

BookInstance model

+ +

Finally, copy the BookInstance schema code shown below and paste it into your ./models/bookinstance.js file. The BookInstance represents a specific copy of a book that someone might borrow and includes information about whether the copy is available or on what date it is expected back, "imprint" or version details.

+ +
var mongoose = require('mongoose');
+
+var Schema = mongoose.Schema;
+
+var BookInstanceSchema = new Schema(
+  {
+    book: { type: Schema.Types.ObjectId, ref: 'Book', required: true }, //reference to the associated book
+    imprint: {type: String, required: true},
+    status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'},
+    due_back: {type: Date, default: Date.now}
+  }
+);
+
+// Virtual for bookinstance's URL
+BookInstanceSchema
+.virtual('url')
+.get(function () {
+  return '/catalog/bookinstance/' + this._id;
+});
+
+//Export model
+module.exports = mongoose.model('BookInstance', BookInstanceSchema);
+ +

The new things we show here are the field options:

+ + + +

Everything else should be familiar from our previous schema.

+ +

Genre model - challenge!

+ +

Open your ./models/genre.js file and create a schema for storing genres (the category of book, e.g. whether it is fiction or non-fiction, romance or military history, etc).

+ +

The definition will be very similar to the other models:

+ + + +

Testing — create some items

+ +

That's it. We now have all models for the site set up!

+ +

In order to test the models (and to create some example books and other items that we can use in our next articles) we'll now run an independent script to create items of each type:

+ +
    +
  1. Download (or otherwise create) the file populatedb.js inside your express-locallibrary-tutorial directory (in the same level as package.json). + +
    +

    Note: You don't need to know how populatedb.js works; it just adds sample data into the database.

    +
    +
  2. +
  3. Enter the following commands in the project root to install the async module that is required by the script (we'll discuss this in later tutorials, ) +
    npm install async
    +
  4. +
  5. Run the script using node in your command prompt, passing in the URL of your MongoDB database (the same one you replaced the insert_your_database_url_here placeholder with, inside app.js earlier): +
    node populatedb <your mongodb url>​​​​
    +
  6. +
  7. The script should run through to completion, displaying items as it creates them in the terminal.
  8. +
+ +
+

Tip: Go to your database on mLab. You should now be able to drill down into individual collections of Books, Authors, Genres and BookInstances, and check out individual documents.

+
+ +

まとめ

+ +

この記事では、Node/Express 上のデータベースと ORM について、また Mongoose のスキーマとモデルの定義方法について多くのことを学びました。次に、この情報を使用して、ローカルライブラリ Web サイト用の BookBookInstanceAuthor、および Genre を設計および実装しました。

+ +

最後に、(スタンドアロンスクリプトを使用して) 多数のインスタンスを作成することによってモデルをテストしました。次の記事では、これらのオブジェクトを表示するためのページの作成について見ていきます。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/routes/index.html b/files/ja/learn/server-side/express_nodejs/routes/index.html new file mode 100644 index 0000000000..bb3e21898b --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/routes/index.html @@ -0,0 +1,640 @@ +--- +title: 'Express チュートリアル Part 4: ルートとコントローラ' +slug: Learn/Server-side/Express_Nodejs/routes +translation_of: Learn/Server-side/Express_Nodejs/routes +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}}
+ +

このチュートリアルでは、地域図書館 Web サイトで最終的に必要となるすべてのリソースエンドポイントに対して、"ダミー" ハンドラ関数を使用してルート (URL 処理コード) を設定します。完成すると、ルート処理コードのためのモジュール構造が得られます。これは、次の記事の実際のハンドラ関数で拡張できます。Express を使ってモジュラールートを作成する方法についても、非常によく理解しています。

+ + + + + + + + + + + + +
前提条件Express/Node のイントロダクションを読んでください。 これまでのチュートリアルのトピック (Express チュートリアル Part 3: データベースを使う (Mongoose を使用)を含む) を完了してください。
目標:単純なルートを作成する方法を理解する。すべての URL エンドポイントを設定します。
+ +

Overview

+ +

In the last tutorial article we defined Mongoose models to interact with the database, and used a (standalone) script to create some initial library records. We can now write the code to present that information to users. The first thing we need to do is determine what information we want to be able to display in our pages, and then define appropriate URLs for returning those resources. Then we're going to need to create the routes (URL handlers) and views (templates) to display those pages.

+ +

The diagram below is provided as a reminder of the main flow of data and things that need to be implemented when handling an HTTP request/response. In addition to the views and routes the diagram shows "controllers" — functions that separate out the code to route requests from the code that actually processes requests.

+ +

As we've already created the models, the main things we'll need to create are:

+ + + +

+ +

Ultimately we might have pages to show lists and detail information for books, genres, authors and bookinstances, along with pages to create, update, and delete records. That's a lot to document in one article. Therefore most of this article will concentrate on setting up our routes and controllers to return "dummy" content. We'll extend the controller methods in our subsequent articles to work with model data.

+ +

The first section below provides a brief "primer" on how to use the Express Router middleware. We'll then use that knowledge in the following sections when we set up the LocalLibrary routes.

+ +

Routes primer

+ +

A route is a section of Express code that associates an HTTP verb (GET, POST, PUT, DELETE, etc.), an URL path/pattern, and a function that is called to handle that pattern.

+ +

There are several ways to create routes. For this tutorial we're going to use the express.Router middleware as it allows us to group the route handlers for a particular part of a site together and access them using a common route-prefix. We'll keep all our library-related routes in a "catalog" module, and, if we add routes for handling user accounts or other functions, we can keep them grouped separately.

+ +
+

Note: We discussed Express application routes briefly in our Express Introduction > Creating route handlers. Other than providing better support for modularization (as discussed in the first subsection below), using Router is very similar to defining routes directly on the Express application object.

+
+ +

The rest of this section provides an overview of how the Router can be used to define the routes.

+ +

Defining and using separate route modules

+ +

The code below provides a concrete example of how we can create a route module and then use it in an Express application.

+ +

First we create routes for a wiki in a module named wiki.js. The code first imports the Express application object, uses it to get a Router object and then adds a couple of routes to it using the get() method. Last of all the module exports the Router object.

+ +
// wiki.js - Wiki route module.
+
+var express = require('express');
+var router = express.Router();
+
+// Home page route.
+router.get('/', function (req, res) {
+  res.send('Wiki home page');
+})
+
+// About page route.
+router.get('/about', function (req, res) {
+  res.send('About this wiki');
+})
+
+module.exports = router;
+
+
+ +
+

Note: Above we are defining our route handler callbacks directly in the router functions. In the LocalLibrary we'll define these callbacks in a separate controller module.

+
+ +

To use the router module in our main app file we first require() the route module (wiki.js). We then call use() on the Express application to add the Router to the middleware handling path, specifying an URL path of 'wiki'.

+ +
var wiki = require('./wiki.js');
+// ...
+app.use('/wiki', wiki);
+ +

The two routes defined in our wiki route module are then accessible from /wiki/ and /wiki/about/.

+ +

Route functions

+ +

Our module above defines a couple of typical route functions. The "about" route (reproduced below) is defined using the Router.get() method, which responds only to HTTP GET requests. The first argument to this method is the URL path while the second is a callback function that will be invoked if an HTTP GET request with the path is received.

+ +
router.get('/about', function (req, res) {
+  res.send('About this wiki');
+})
+
+ +

The callback takes three arguments (usually named as shown: req, res, next), that will contain the HTTP Request object, HTTP response, and the next function in the middleware chain.

+ +
+

Note: Router functions are Express middleware, which means that they must either complete (respond to) the request or call the next function in the chain. In the case above we complete the request using send(), so the next argument is not used (and we choose not to specify it).

+ +

The router function above takes a single callback, but you can specify as many callback arguments as you want, or an array of callback functions. Each function is part of the middleware chain, and will be called in the order it is added to the chain (unless a preceding function completes the request).

+
+ +

The callback function here calls send() on the response to return the string "About this wiki" when we receive a GET request with the path ('/about'). There are a number of other response methods for ending the request/response cycle. For example, you could call res.json() to send a JSON response or res.sendFile() to send a file. The response method that we'll be using most often as we build up the library is render(), which creates and returns HTML files using templates and data—we'll talk a lot more about that in a later article!

+ +

HTTP verbs

+ +

The example routes above use the Router.get() method to respond to HTTP GET requests with a certain path.

+ +

The Router also provides route methods for all the other HTTP verbs, that are mostly used in exactly the same way: post(), put(), delete(), options(), trace(), copy(), lock(), mkcol(), move(), purge(), propfind(), proppatch(), unlock(), report(), mkactivity(), checkout(), merge(), m-search(), notify(), subscribe(), unsubscribe(), patch(), search(), and connect().

+ +

For example, the code below behaves just like the previous /about route, but only responds to HTTP POST requests.

+ +
router.post('/about', function (req, res) {
+  res.send('About this wiki');
+})
+ +

Route paths

+ +

The route paths define the endpoints at which requests can be made. The examples we've seen so far have just been strings, and are used exactly as written: '/', '/about', '/book', '/any-random.path'.

+ +

Route paths can also be string patterns. String patterns use a subset of regular expression syntax to define patterns of endpoints that will be matched. The subset is listed below (note that the hyphen (-) and the dot (.) are interpreted literally by string-based paths):

+ + + +

The route paths can also be JavaScript regular expressions. For example, the route path below will match catfish and dogfish, but not catflap, catfishhead, and so on. Note that the path for a regular expression uses regular expression syntax (it is not a quoted string as in the previous cases).

+ +
app.get(/.*fish$/, function (req, res) {
+  ...
+})
+ +
+

Note: Most of our routes for the LocalLibrary will simply use strings and not string patterns and regular expressions. We'll also use route parameters as discussed in the next section.

+
+ +

Route parameters

+ +

Route parameters are named URL segments used to capture the values specified at their position in the URL. The named segments are prefixed with a colon and then the name (e.g. /:your_parameter_name/. The captured values are stored in the req.params object using the parameter names as keys (e.g. req.params.your_parameter_name).

+ +

So for example, consider a URL encoded to contain information about users and books: http://localhost:3000/users/34/books/8989. We can extract this information as shown below, with the userId and bookId path parameters:

+ +
app.get('/users/:userId/books/:bookId', function (req, res) {
+  // Access userId via: req.params.userId
+  // Access bookId via: req.params.bookId
+  res.send(req.params);
+})
+
+ +

The names of route parameters must be made up of “word characters” (A-Z, a-z, 0-9, and _).

+ +
+

Note: The URL /book/create will be matched by a route like /book/:bookId (which will extract a "bookId" value of 'create'). The first route that matches an incoming URL will be used, so if you want to process /book/create URLs separately, their route handler must be defined before your /book/:bookId route.

+
+ +

That's all you need to get started with routes - if needed you can find more information in the Express docs: Basic routing and Routing guide. The following sections show how we'll set up our routes and controllers for the LocalLibrary.

+ +

Routes needed for the LocalLibrary

+ +

The URLs that we're ultimately going to need for our pages are listed below, where object is replaced by the name of each of our models (book, bookinstance, genre, author), objects is the plural of object, and id is the unique instance field (_id) that is given to each Mongoose model instance by default.

+ + + +

The first home page and list pages don't encode any additional information. While the results returned will depend on the model type and the content in the database, the queries run to get the information will always be the same (similarly the code run for object creation will always be similar).

+ +

By contrast the other URLs are used to act on a specific document/model instance—these encode the identity of the item in the URL (shown as <id> above). We'll use path parameters to extract the encoded information and pass it to the route handler (and in a later article we'll use this to dynamically determine what information to get from the database). By encoding the information in our URL we only need one route for every resource of a particular type (e.g. one route to handle the display of every single book item).

+ +
+

Note: Express allows you to construct your URLs any way you like — you can encode information in the body of the URL as shown above or use URL GET parameters (e.g. /book/?id=6). Whichever approach you use, the URLs should be kept clean, logical and readable (check out the W3C advice here).

+
+ +

Next we create our route handler callback functions and route code for all the above URLs.

+ +

Create the route-handler callback functions

+ +

Before we define our routes, we'll first create all the dummy/skeleton callback functions that they will invoke. The callbacks will be stored in separate "controller" modules for Books, BookInstances, Genres, and Authors (you can use any file/module structure, but this seems an appropriate granularity for this project).

+ +

Start by creating a folder for our controllers in the project root (/controllers) and then create separate controller files/modules for handling each of the models:

+ +
/express-locallibrary-tutorial  //the project root
+  /controllers
+    authorController.js
+    bookController.js
+    bookinstanceController.js
+    genreController.js
+ +

Author controller

+ +

Open the /controllers/authorController.js file and type in the following code:

+ +
var Author = require('../models/author');
+
+// Display list of all Authors.
+exports.author_list = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author list');
+};
+
+// Display detail page for a specific Author.
+exports.author_detail = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author detail: ' + req.params.id);
+};
+
+// Display Author create form on GET.
+exports.author_create_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author create GET');
+};
+
+// Handle Author create on POST.
+exports.author_create_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author create POST');
+};
+
+// Display Author delete form on GET.
+exports.author_delete_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author delete GET');
+};
+
+// Handle Author delete on POST.
+exports.author_delete_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author delete POST');
+};
+
+// Display Author update form on GET.
+exports.author_update_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author update GET');
+};
+
+// Handle Author update on POST.
+exports.author_update_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Author update POST');
+};
+
+ +

The module first requires the model that we'll later be using to access and update our data. It then exports functions for each of the URLs we wish to handle (the create, update and delete operations use forms, and hence also have additional methods for handling form post requests — we'll discuss those methods in the "forms article" later on).

+ +

All the functions have the standard form of an Express middleware function, with arguments for the request, response, and the next function to be called if the method does not complete the request cycle (in all these cases it does!). The methods simply return a string indicating that the associated page has not yet been created. If a controller function is expected to receive path parameters, these are output in the message string (see req.params.id above).

+ +

BookInstance controller

+ +

Open the /controllers/bookinstanceController.js file and copy in the following code (this follows an identical pattern to the Author controller module):

+ +
var BookInstance = require('../models/bookinstance');
+
+// Display list of all BookInstances.
+exports.bookinstance_list = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance list');
+};
+
+// Display detail page for a specific BookInstance.
+exports.bookinstance_detail = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance detail: ' + req.params.id);
+};
+
+// Display BookInstance create form on GET.
+exports.bookinstance_create_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance create GET');
+};
+
+// Handle BookInstance create on POST.
+exports.bookinstance_create_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance create POST');
+};
+
+// Display BookInstance delete form on GET.
+exports.bookinstance_delete_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance delete GET');
+};
+
+// Handle BookInstance delete on POST.
+exports.bookinstance_delete_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance delete POST');
+};
+
+// Display BookInstance update form on GET.
+exports.bookinstance_update_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance update GET');
+};
+
+// Handle bookinstance update on POST.
+exports.bookinstance_update_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: BookInstance update POST');
+};
+
+ +

Genre controller

+ +

Open the /controllers/genreController.js file and copy in the following text (this follows an identical pattern to the Author and BookInstance files):

+ +
var Genre = require('../models/genre');
+
+// Display list of all Genre.
+exports.genre_list = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre list');
+};
+
+// Display detail page for a specific Genre.
+exports.genre_detail = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre detail: ' + req.params.id);
+};
+
+// Display Genre create form on GET.
+exports.genre_create_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre create GET');
+};
+
+// Handle Genre create on POST.
+exports.genre_create_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre create POST');
+};
+
+// Display Genre delete form on GET.
+exports.genre_delete_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre delete GET');
+};
+
+// Handle Genre delete on POST.
+exports.genre_delete_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre delete POST');
+};
+
+// Display Genre update form on GET.
+exports.genre_update_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre update GET');
+};
+
+// Handle Genre update on POST.
+exports.genre_update_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Genre update POST');
+};
+
+ +

Book controller

+ +

Open the /controllers/bookController.js file and copy in the following code. This follows the same pattern as the other controller modules, but additionally has an index() function for displaying the site welcome page:

+ +
var Book = require('../models/book');
+
+exports.index = function(req, res) {
+    res.send('NOT IMPLEMENTED: Site Home Page');
+};
+
+// Display list of all books.
+exports.book_list = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book list');
+};
+
+// Display detail page for a specific book.
+exports.book_detail = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book detail: ' + req.params.id);
+};
+
+// Display book create form on GET.
+exports.book_create_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book create GET');
+};
+
+// Handle book create on POST.
+exports.book_create_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book create POST');
+};
+
+// Display book delete form on GET.
+exports.book_delete_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book delete GET');
+};
+
+// Handle book delete on POST.
+exports.book_delete_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book delete POST');
+};
+
+// Display book update form on GET.
+exports.book_update_get = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book update GET');
+};
+
+// Handle book update on POST.
+exports.book_update_post = function(req, res) {
+    res.send('NOT IMPLEMENTED: Book update POST');
+};
+
+ +

Create the catalog route module

+ +

Next we create routes for all the URLs needed by the LocalLibrary website, which will call the controller functions we defined in the previous section.

+ +

The skeleton already has a ./routes folder containing routes for the index and users. Create another route file — catalog.js — inside this folder, as shown.

+ +
/express-locallibrary-tutorial //the project root
+  /routes
+    index.js
+    users.js
+    catalog.js
+ +

Open /routes/catalog.js and copy in the code below:

+ +
var express = require('express');
+var router = express.Router();
+
+// Require controller modules.
+var book_controller = require('../controllers/bookController');
+var author_controller = require('../controllers/authorController');
+var genre_controller = require('../controllers/genreController');
+var book_instance_controller = require('../controllers/bookinstanceController');
+
+/// BOOK ROUTES ///
+
+// GET catalog home page.
+router.get('/', book_controller.index);
+
+// GET request for creating a Book. NOTE This must come before routes that display Book (uses id).
+router.get('/book/create', book_controller.book_create_get);
+
+// POST request for creating Book.
+router.post('/book/create', book_controller.book_create_post);
+
+// GET request to delete Book.
+router.get('/book/:id/delete', book_controller.book_delete_get);
+
+// POST request to delete Book.
+router.post('/book/:id/delete', book_controller.book_delete_post);
+
+// GET request to update Book.
+router.get('/book/:id/update', book_controller.book_update_get);
+
+// POST request to update Book.
+router.post('/book/:id/update', book_controller.book_update_post);
+
+// GET request for one Book.
+router.get('/book/:id', book_controller.book_detail);
+
+// GET request for list of all Book items.
+router.get('/books', book_controller.book_list);
+
+/// AUTHOR ROUTES ///
+
+// GET request for creating Author. NOTE This must come before route for id (i.e. display author).
+router.get('/author/create', author_controller.author_create_get);
+
+// POST request for creating Author.
+router.post('/author/create', author_controller.author_create_post);
+
+// GET request to delete Author.
+router.get('/author/:id/delete', author_controller.author_delete_get);
+
+// POST request to delete Author.
+router.post('/author/:id/delete', author_controller.author_delete_post);
+
+// GET request to update Author.
+router.get('/author/:id/update', author_controller.author_update_get);
+
+// POST request to update Author.
+router.post('/author/:id/update', author_controller.author_update_post);
+
+// GET request for one Author.
+router.get('/author/:id', author_controller.author_detail);
+
+// GET request for list of all Authors.
+router.get('/authors', author_controller.author_list);
+
+/// GENRE ROUTES ///
+
+// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
+router.get('/genre/create', genre_controller.genre_create_get);
+
+//POST request for creating Genre.
+router.post('/genre/create', genre_controller.genre_create_post);
+
+// GET request to delete Genre.
+router.get('/genre/:id/delete', genre_controller.genre_delete_get);
+
+// POST request to delete Genre.
+router.post('/genre/:id/delete', genre_controller.genre_delete_post);
+
+// GET request to update Genre.
+router.get('/genre/:id/update', genre_controller.genre_update_get);
+
+// POST request to update Genre.
+router.post('/genre/:id/update', genre_controller.genre_update_post);
+
+// GET request for one Genre.
+router.get('/genre/:id', genre_controller.genre_detail);
+
+// GET request for list of all Genre.
+router.get('/genres', genre_controller.genre_list);
+
+/// BOOKINSTANCE ROUTES ///
+
+// GET request for creating a BookInstance. NOTE This must come before route that displays BookInstance (uses id).
+router.get('/bookinstance/create', book_instance_controller.bookinstance_create_get);
+
+// POST request for creating BookInstance.
+router.post('/bookinstance/create', book_instance_controller.bookinstance_create_post);
+
+// GET request to delete BookInstance.
+router.get('/bookinstance/:id/delete', book_instance_controller.bookinstance_delete_get);
+
+// POST request to delete BookInstance.
+router.post('/bookinstance/:id/delete', book_instance_controller.bookinstance_delete_post);
+
+// GET request to update BookInstance.
+router.get('/bookinstance/:id/update', book_instance_controller.bookinstance_update_get);
+
+// POST request to update BookInstance.
+router.post('/bookinstance/:id/update', book_instance_controller.bookinstance_update_post);
+
+// GET request for one BookInstance.
+router.get('/bookinstance/:id', book_instance_controller.bookinstance_detail);
+
+// GET request for list of all BookInstance.
+router.get('/bookinstances', book_instance_controller.bookinstance_list);
+
+module.exports = router;
+
+ +

The module requires Express and then uses it to create a Router object. The routes are all set up on the router, which is then exported.

+ +

The routes are defined either using .get() or .post() methods on the router object. All the paths are defined using strings (we don't use string patterns or regular expressions). Routes that act on some specific resource (e.g. book) use path parameters to get the object id from the URL.

+ +

The handler functions are all imported from the controller modules we created in the previous section.

+ +

Update the index route module

+ +

We've set up all our new routes, but we still have a route to the original page. Let's instead redirect this to the new index page that we've created at the path '/catalog'.

+ +

Open /routes/index.js and replace the existing route with the function below.

+ +
// GET home page.
+router.get('/', function(req, res) {
+  res.redirect('/catalog');
+});
+ +
+

Note: This is our first use of the redirect() response method. This redirects to the specified page, by default sending HTTP status code "302 Found". You can change the status code returned if needed, and supply either absolute or relative paths.

+
+ +

Update app.js

+ +

The last step is to add the routes to the middleware chain. We do this in app.js.

+ +

Open app.js and require the catalog route below the other routes (add the third line shown below, underneath the other two):

+ +
var indexRouter = require('./routes/index');
+var usersRouter = require('./routes/users');
+var catalogRouter = require('./routes/catalog');  //Import routes for "catalog" area of site
+ +

Next, add the catalog route to the middleware stack below the other routes (add the third line shown below, underneath the other two):

+ +
app.use('/', indexRouter);
+app.use('/users', usersRouter);
+app.use('/catalog', catalogRouter);  // Add catalog routes to middleware chain.
+ +
+

Note: We have added our catalog module at a path '/catalog'. This is prepended to all of the paths defined in the catalog module. So for example, to access a list of books, the URL will be: /catalog/books/.

+
+ +

That's it. We should now have routes and skeleton functions enabled for all the URLs that we will eventually support on the LocalLibrary website.

+ +

Testing the routes

+ +

To test the routes, first start the website using your usual approach

+ + + +

Then navigate to a number of LocalLibrary URLs, and verify that you don't get an error page (HTTP 404). A small set of URLs are listed below for your convenience:

+ + + +

まとめ

+ +

これでサイトのすべてのルートを、後の記事で完全な実装を追加することができるダミーのコントローラ関数もあわせて作成しました。その過程で、Express ルートについての多くの基本的な情報、そしてルートとコントローラを構築するためのいくつかのアプローチを学びました。

+ +

次の記事では、モデルに格納されているビュー (テンプレート) と情報を使用して、サイト用の適切なウェルカムページを作成します。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/skeleton_website/index.html b/files/ja/learn/server-side/express_nodejs/skeleton_website/index.html new file mode 100644 index 0000000000..5b0acfecd2 --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/skeleton_website/index.html @@ -0,0 +1,512 @@ +--- +title: 'Express チュートリアル Part 2: スケルトン Web サイトの作成' +slug: Learn/Server-side/Express_Nodejs/skeleton_website +tags: + - CodingScripting + - Express + - Node + - イントロダクション + - サーバサイド + - 初心者 + - 学習 + - 開発環境 +translation_of: Learn/Server-side/Express_Nodejs/skeleton_website +--- +
{{LearnSidebar}}
+ +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}}

+ +

この Express チュートリアルの2回目の記事では、どのようにして "スケルトン" Web サイトプロジェクトを作成し、サイト固有のルート、テンプレート/ビュー、およびデータベース呼び出しを追加するかを説明します。

+ + + + + + + + + + + + +
前提条件:Node 開発環境を設定します。Express チュートリアルを確認してください。
目標:Express Application Generator を使用して独自の新しい Web サイトプロジェクトを開始できるようにする。
+ +

概要

+ +

この記事では Express Application Generator ツールを使用して "スケルトン" Web サイトを作成する方法を説明します。このツールには、サイト固有のルート、ビュー/テンプレート、およびデータベース呼び出しを追加できます。この場合は、このツールを使用してローカルライブラリ Web サイト用のフレームワークを作成し、後でそのサイトに必要な他のすべてのコードを追加します。プロセスは非常に単純で、必要に応じてサイトのテンプレートエンジンと CSS ジェネレータを指定して、新しいプロジェクト名を指定してコマンドラインでジェネレータを呼び出すだけです。

+ +

次のセクションでは、アプリケーションジェネレータを呼び出す方法を示し、さまざまな ビュー/CSS オプションについて簡単に説明します。また、スケルトン Web サイトの構造についても説明します。最後に、Web サイトを実行してそれが機能することを確認する方法を紹介します。

+ +
+

メモ: Express アプリケーションジェネレータは、Express アプリケーション用の唯一のジェネレータというわけではありません。また、生成されたプロジェクトはファイルやディレクトリを構造化する唯一実行可能な方法というわけではありません。しかしながら、生成されたサイトは、拡張と理解が容易なモジュール構造を持っています。最小限の Express アプリケーションについては、Hello world の例 (Express ドキュメント) を参照してください。

+
+ +

アプリケーションジェネレータを使用する

+ +

Node 開発環境の設定の一部として、ジェネレータをすでにインストールしているはずです。念のため、NPM パッケージマネージャを使用して、サイト全体にジェネレータツールをインストールします。

+ +
npm install express-generator -g
+ +

ジェネレータにはいくつかのオプションがあり、それらは --help (または -h) コマンドを使用してコマンドラインで表示できます。

+ +
> express --help
+
+  Usage: express [options] [dir]
+
+  Options:
+
+    -h, --help           output usage information
+        --version        output the version number
+    -e, --ejs            add ejs engine support
+        --pug            add pug engine support
+        --hbs            add handlebars engine support
+    -H, --hogan          add hogan.js engine support
+    -v, --view <engine>  add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
+    -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
+        --git            add .gitignore
+    -f, --force          force on non-empty directory
+
+ +

Jade ビューエンジンとプレーン CSS を使用して、express を指定するだけで現在のディレクトリ内にプロジェクトを作成できます (ディレクトリ名を指定すると、プロジェクトはその名前のサブフォルダに作成されます)。

+ +
express
+ +

--view を使用してビュー (テンプレート) エンジンを選択したり、--css を使用して CSS 生成エンジンを選択したりすることもできます。

+ +
+

メモ: テンプレートエンジンを選択するためのその他のオプション (--hogan--ejs--hbs など) は推奨されません。--view (または -v)を使用してください。

+
+ +

どのビューエンジンを使うべきですか?

+ +

Express Application Generator を使用すると、EJSHbsPug (Jade)、TwigVash など、一般的なビュー/テンプレートエンジンを多数設定できます。ただし、ビューオプションを指定しない場合はデフォルトで Jade が選択されます。 Express 自体は、一般的な他のテンプレート言語の多くをサポートすることができます。

+ +
+

メモ: ジェネレータでサポートされていないテンプレートエンジンを使用したい場合は、Express でテンプレートエンジンを使用する (Express ドキュメント) およびターゲットビューエンジンのドキュメントを参照してください。

+
+ +

一般的に、あなたが必要とするすべての機能を提供し、あなたがより早く生産的になることを可能にするテンプレートエンジンを選ぶべきです ー もしくは言い換えれば、同じ方法で、他のコンポーネントを選択するということです。テンプレートエンジンを比較する際に考慮すべき点がいくつかあります。

+ + + +
+

Tip: インターネット上には、さまざまなオプションを比較するのに役立つ多くのリソースがあります。

+
+ +

このプロジェクトでは、Pug テンプレートエンジン (これは最近名前が変更された Jade エンジンです) を使用します。これは最も一般的な Express/JavaScript テンプレート言語の1つで、ジェネレータによってそのままサポートされているためです。

+ +

どの CSS スタイルシートエンジンを使うべきですか?

+ +

Express Application Generator を使用すると、最も一般的なCSSスタイルシートエンジン (LESSSASSCompassStylus) を使用するように構成されたプロジェクトを作成できます。

+ +
+

メモ: CSS にはいくつかの制限があり、特定のタスクを困難にします。CSS スタイルシートエンジンを使用すると、CSS を定義するためのより強力な構文を使用してから、ブラウザで使用するためにその定義を通常の CSS にコンパイルできます。

+
+ +

テンプレートエンジンと同様に、チームを最も生産的にすることができるスタイルシートエンジンを使用する必要があります。このプロジェクトでは、CSS 要件が他のものを使用することを正当化するのに十分に複雑ではないため、通常のCSS (デフォルト) を使用します。

+ +

どのデータベースを使うべきですか?

+ +

生成されたコードはデータベースを一切使用しません。 Express アプリケーションは、Node によってサポートされている任意のデータベースメカニズムを使用できます (Express自体はデータベース管理のための特定の追加の動作や要件を定義していません)。

+ +

データベースと統合する方法については、後の記事で説明します。

+ +

プロジェクトを作成する

+ +

これから作成するサンプルのローカルライブラリアプリでは、Pug テンプレートライブラリを使用し、CSS スタイルシートエンジンを使用しないで、express-locallibrary-tutorial という名前のプロジェクトを作成します。

+ +

まず、プロジェクトを作成する場所に移動し、次に示すようにコマンドプロンプトで Express Application Generator を実行します。

+ +
express express-locallibrary-tutorial --view=pug
+
+ +

ジェネレータはプロジェクトのファイルを作成 (そして一覧表示) します。

+ +
   create : express-locallibrary-tutorial
+   create : express-locallibrary-tutorial/package.json
+   create : express-locallibrary-tutorial/app.js
+   create : express-locallibrary-tutorial/public/images
+   create : express-locallibrary-tutorial/public
+   create : express-locallibrary-tutorial/public/stylesheets
+   create : express-locallibrary-tutorial/public/stylesheets/style.css
+   create : express-locallibrary-tutorial/public/javascripts
+   create : express-locallibrary-tutorial/routes
+   create : express-locallibrary-tutorial/routes/index.js
+   create : express-locallibrary-tutorial/routes/users.js
+   create : express-locallibrary-tutorial/views
+   create : express-locallibrary-tutorial/views/index.pug
+   create : express-locallibrary-tutorial/views/layout.pug
+   create : express-locallibrary-tutorial/views/error.pug
+   create : express-locallibrary-tutorial/bin
+   create : express-locallibrary-tutorial/bin/www
+
+   install dependencies:
+     > cd express-locallibrary-tutorial && npm install
+
+   run the app:
+     > SET DEBUG=express-locallibrary-tutorial:* & npm start
+ +

出力の最後に、ジェネレータは依存関係をどのようにインストールするか (package.json ファイルにリストされているとおり)、次にアプリケーションを実行する方法についての指示を提供します (上記の手順は Windows 用です。Linux/ macOS では若干異なります)。

+ +

スケルトン Web サイトを実行する

+ +

これで、完全なスケルトンプロジェクトが完成しました。 ウェブサイトは実際にはそれほど多くは行っていませんが、それがどのように機能するかを示すために実行する価値があります。

+ +
    +
  1. まず、依存関係をインストールします (install コマンドはプロジェクトの package.json ファイルにリストされているすべての依存関係パッケージを取得します) + +
    cd express-locallibrary-tutorial
    +npm install
    +
  2. +
  3. その後、アプリケーションを実行します +
      +
    • Windows では、次のコマンドを使用します +
      SET DEBUG=express-locallibrary-tutorial:* & npm start
      +
    • +
    • macOS または Linux では、次のコマンドを使用します +
      DEBUG=express-locallibrary-tutorial:* npm start
      +
      +
    • +
    +
  4. +
  5. その後、ブラウザに http://localhost:3000/ をロードしてアプリにアクセスします
  6. +
+ +

次のようなブラウザページが表示されるはずです。

+ +

Browser for default Express app generator website

+ +

自分自身で localhost:3000 に提供している、動作する Express アプリケーションがあります。

+ +
+

メモ: npm start コマンドを使用してアプリを起動することもできます。示されているように DEBUG 変数を指定すると、コンソールロギング/デバッグが有効になります。たとえば、上記のページにアクセスすると、次のようなデバッグ出力が表示されます。

+ +
>SET DEBUG=express-locallibrary-tutorial:* & npm start
+
+> express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial
+> node ./bin/www
+
+  express-locallibrary-tutorial:server Listening on port 3000 +0ms
+GET / 200 288.474 ms - 170
+GET /stylesheets/style.css 200 5.799 ms - 111
+GET /favicon.ico 404 34.134 ms - 1335
+
+ +

ファイルの変更時にサーバの再起動を有効にする

+ +

Express Web サイトに加えた変更は、現在のサーバを再起動するまで表示されません。変更を加えるたびにサーバを停止して再起動する必要があることはすぐに非常に苛立たしいものになるため、必要に応じてサーバの再起動を自動化することに時間をかける価値があります。

+ +

この目的のための最も簡単なツールの1つが nodemon です。 これは通常 "ツール" としてグローバルにインストールされますが、ここでは開発者の依存関係としてローカルにインストールして使用するので、プロジェクトを操作する開発者はアプリケーションをインストールするときに自動的に取得されます。スケルトンプロジェクトのルートディレクトリで次のコマンドを使用します。

+ +
npm install --save-dev nodemon
+ +

プロジェクトの package.json ファイルだけでなく、自分のマシンにグローバルに nodemon をインストールする場合は、次のようにします。

+ +
npm install -g nodemon
+ +

プロジェクトの package.json ファイルを開くと、この依存関係を持つ新しいセクションが表示されます。

+ +
  "devDependencies": {
+    "nodemon": "^1.14.11"
+  }
+
+ +

このツールはグローバルにはインストールされていないので (パスに追加しない限り) コマンドラインから起動することはできませんが、NPM はインストールされているパッケージをすべて知っているので、NPM スクリプトから呼び出すことができます。package.json の scripts セクションを見つけます。最初は "start" で始まる1行が含まれています。その行の末尾にカンマを入れて、以下に見られる "devstart" 行を追加することによってそれを更新します。

+ +
  "scripts": {
+    "start": "node ./bin/www",
+    "devstart": "nodemon ./bin/www"
+  },
+
+ +

devstart コマンドを指定して、以前とほぼ同じ方法でサーバーを起動することができます。

+ + + +
+

メモ: プロジェクト内のファイルを編集した場合は、サーバが再起動します (またはコマンドプロンプトで rs と入力していつでも再起動できます)。あなたはまだページを更新するためにブラウザを再読み込みする必要があるでしょう。

+ +

"start" は実際には名前付きスクリプトにマップされた NPM コマンドなので、ここでは単に npm start ではなく  "npm run <scriptname>" を呼び出す必要があります。起動スクリプトのコマンドを置き換えることもできますが、開発中は nodemon を使用するだけなので、新しいスクリプトコマンドを作成するのが理にかなっています。

+
+ +

生成されたプロジェクト

+ +

今作成したプロジェクトを見てみましょう。

+ +

ディレクトリ構造

+ +

生成されたプロジェクトは、依存関係をインストールしたので、次のようなファイル構造になります (ファイルは "/" が前に付いていない項目です)。package.json ファイルは、アプリケーションの依存関係とその他の情報を定義します。また、アプリケーションのエントリポイントである JavaScript ファイル /bin/www を呼び出す起動スクリプトも定義されています。これにより、アプリケーションのエラー処理がいくつか設定され、その後 app.js が読み込まれて残りの作業が行われます。 アプリのルートは routes/ ディレクトリの下の別々のモジュールに格納されています。 テンプレートは /views ディレクトリの下に格納されています。

+ +
/express-locallibrary-tutorial
+    app.js
+    /bin
+        www
+    package.json
+    /node_modules
+        [about 4,500 subdirectories and files]
+    /public
+        /images
+        /javascripts
+        /stylesheets
+            style.css
+    /routes
+        index.js
+        users.js
+    /views
+        error.pug
+        index.pug
+        layout.pug
+
+
+ +

次のセクションでは、ファイルについてもう少し詳しく説明します。

+ +

package.json

+ +

package.json ファイルは、アプリケーションの依存関係とその他の情報を定義します。

+ +
{
+  "name": "express-locallibrary-tutorial",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "start": "node ./bin/www",
+    "devstart": "nodemon ./bin/www"
+  },
+  "dependencies": {
+    "cookie-parser": "~1.4.3",
+    "debug": "~2.6.9",
+    "express": "~4.16.2",
+    "morgan": "~1.9.0",
+    "pug": "~2.0.0-rc.4",
+    "serve-favicon": "~2.4.5"
+  },
+  "devDependencies": {
+    "nodemon": "^1.14.11"
+  }
+}
+
+ +

依存関係には、express パッケージと選択したビューエンジン (pug) 用のパッケージが含まれます。さらに、多くの Web アプリケーションで役立つ次のパッケージがあります。

+ + + +

scripts セクションは "start" スクリプトを定義します。これは、npm start を呼び出してサーバを起動するときに呼び出すスクリプトです。スクリプトの定義から、これは実際に JavaScript ファイル ./bin/www with node を開始することがわかります。また、"devstart" スクリプトも定義しています。これは、代わりに npm run devstart を呼び出すときに呼び出されます。これは同じ ./bin/www ファイルを開始しますが、node ではなく nodemon を使用します。

+ +
  "scripts": {
+    "start": "node ./bin/www",
+    "devstart": "nodemon ./bin/www"
+  },
+
+ +

www ファイル

+ +

ファイル /bin/www がアプリケーションのエントリポイントです。 これが最初に行うことは、express() アプリケーションオブジェクトを設定して返す "本物の" アプリケーションエントリポイント (プロジェクトルート内の app.js) であるrequire() です。

+ +
#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var app = require('../app');
+
+ +
+

メモ: require() は、現在のファイルにモジュールをインポートするために使われるグローバル Node 関数です。 ここでは相対パスを使用し、オプションの (.js) ファイル拡張子を省略して app.js モジュールを指定します。

+
+ +

このファイルのコードの残りの部分では、特定のポート (環境変数で定義されているか、変数が定義されていない場合は 3000 で定義されている) に設定された app を使用してNode HTTP サーバをセットアップします。 今のところ、コードについて他に何も知る必要はありません (このファイル内のすべてが "定型句" です) が、興味があればお気軽にレビューしてください。

+ +

app.js

+ +

このファイルは、express アプリケーションオブジェクト (慣例では app という名前) を作成し、さまざまな設定とミドルウェアを使用してアプリケーションを設定してから、モジュールからアプリケーションをエクスポートします。以下のコードは、アプリオブジェクトを作成およびエクスポートするファイルの一部だけを示しています。

+ +
var express = require('express');
+var app = express();
+...
+module.exports = app;
+
+ +

上記の www エントリポイントファイルに戻ると、このファイルがインポートされたときに呼び出し元に渡されるのは、この module.exports オブジェクトです。

+ +

app.js ファイルを詳しく見ていきましょう。まず、NPM を使用してアプリケーション用に以前にダウンロードしたexpress、serve-favicon、morgan、cookie-parser など、require() を使用していくつかの便利な Node ライブラリをファイルにインポートします。path は、ファイルとディレクトリのパスを解析するためのコア Node ライブラリです。

+ +
var express = require('express');
+var path = require('path');
+var favicon = require('serve-favicon');
+var logger = require('morgan');
+var cookieParser = require('cookie-parser');
+
+ +

それからroutesディレクトリから require() モジュールを呼び出します。これらのモジュール/ファイルには、関連する "ルート" (URL パス) の特定のセットを処理するためのコードが含まれています。たとえばライブラリ内のすべての本をリストするためにスケルトンアプリケーションを拡張するときは、本関連のルートを処理するための新しいファイルを追加します。

+ +
var indexRouter = require('./routes/index');
+var usersRouter = require('./routes/users');
+
+ +
+

メモ: この時点で、モジュールをインポートしたばかりです。 実際にはまだそのルートを使用していません (これはファイルの少し下まで行われます)。

+
+ +

次に、インポートした Express モジュールを使ってアプリオブジェクトを作成し、それを使ってビュー (テンプレート) エンジンを設定します。エンジンの設定には2つの部分があります。まず、 'views' の値を設定して、テンプレートが保存されるフォルダを指定します (この場合はサブフォルダの /views)。それから 'view engine' の値を設定してテンプレートライブラリ (この場合は "pug") を指定します。

+ +
var app = express();
+
+// view engine setup
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'pug');
+
+ +

次の一連の関数は app.use() を呼び出してミドルウェアライブラリをリクエスト処理チェーンに追加します。以前インポートしたサードパーティのライブラリに加えて、Express がプロジェクトルートの /public ディレクトリにあるすべての静的ファイルを処理するようにするために、express.static ミドルウェアを使用します。

+ +
// uncomment after placing your favicon in /public
+//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
+app.use(logger('dev'));
+app.use(express.json());
+app.use(express.urlencoded({ extended: false }));
+app.use(cookieParser());
+app.use(express.static(path.join(__dirname, 'public')));
+
+ +

他のすべてのミドルウェアがセットアップされたので、(以前にインポートした) ルート処理コードをリクエスト処理チェーンに追加します。 インポートされたコードは、サイトのさまざまな部分に特定のルートを定義します。

+ +
app.use('/', indexRouter);
+app.use('/users', usersRouter);
+
+ +
+

メモ: 上記で指定されたパス ('/' と '/users') は、インポートされたファイルで定義されているルートの接頭辞として扱われます。たとえば、インポートされたユーザーモジュールが /profile のルートを定義している場合は、/users/profile でそのルートにアクセスします。 ルートの詳細については後の記事で説明します。

+
+ +

ファイルの最後のミドルウェアは、エラーと HTTP 404 レスポンス用のハンドラメソッドを追加します。

+ +
// catch 404 and forward to error handler
+app.use(function(req, res, next) {
+  var err = new Error('Not Found');
+  err.status = 404;
+  next(err);
+});
+
+// error handler
+app.use(function(err, req, res, next) {
+  // set locals, only providing error in development
+  res.locals.message = err.message;
+  res.locals.error = req.app.get('env') === 'development' ? err : {};
+
+  // render the error page
+  res.status(err.status || 500);
+  res.render('error');
+});
+
+ +

Express アプリケーションオブジェクト (app) が完全に設定されました。最後のステップはそれをモジュールのエクスポートに追加することです (これは /bin/www によってインポートされることを可能にするものです)。

+ +
module.exports = app;
+ +

ルート

+ +

ルートファイル /routes/users.js を以下に示します (ルートファイルは同様の構造を共有しているので、index.js も表示する必要はありません)。まず、express モジュールをロードし、それを使って express.Router オブジェクトを取得します。それからそのオブジェクトのルートを指定し、最後にモジュールからルーターをエクスポートします (これがファイルが app.js にインポートされることを可能にするものです)。

+ +
var express = require('express');
+var router = express.Router();
+
+/* GET users listing. */
+router.get('/', function(req, res, next) {
+  res.send('respond with a resource');
+});
+
+module.exports = router;
+
+ +

このルートは、正しいパターンの HTTP GET リクエストが検出されたときに必ず呼び出されるコールバックを定義します。一致パターンは、モジュールのインポート時に指定された経路 ('/users') と、このファイルで定義されているもの ('/') です。 つまり、このルートは /users/ の URL が受信されたときに使用されます。

+ +
+

Tip: これを試すには、node を使用してサーバを実行し、ブラウザの URL (http://localhost:3000/users/) にアクセスしてください。「リソースで応答してください」というメッセージが表示されます。

+
+ +

上記の関心事の1つは、コールバック関数が3番目の引数 'next' を持ち、したがって単純なルートコールバックではなくミドルウェア関数であることです。このコードでは現在 next 引数を使用していませんが、'/' ルートパスに複数のルートハンドラを追加したい場合、将来的には役に立つかもしれません。

+ +

ビュー (テンプレート)

+ +

ビュー (テンプレート) は /views ディレクトリ (app.js で指定されている) に保存され、ファイル拡張子 .pug が与えられます。Response.render() メソッドは、オブジェクトに渡された名前付き変数の値とともに指定されたテンプレートをレンダリングし、その結果をレスポンスとして送信するために使用されます。以下の /routes/index.js のコードでは、テンプレート変数 "title" を渡した "index" テンプレートを使用して、そのルートがどのようにレスポンスをレンダリングするかを確認できます。

+ +
/* GET home page. */
+router.get('/', function(req, res) {
+  res.render('index', { title: 'Express' });
+});
+
+ +

上記のルートに対応するテンプレートを以下に示します(index.pug)。構文については後で詳しく説明します。今のところ知る必要があるのは、(変数 'Express' を持つ) title変数がテンプレートの指定された場所に挿入されることだけです。

+ +
extends layout
+
+block content
+  h1= title
+  p Welcome to #{title}
+
+ +

自分自身で挑戦

+ +

/routes/users.js に新しいルートを作成し、URL /users/cool/ に "You're so cool" というテキストを表示します。 サーバーを実行し、ブラウザで http://localhost:3000/users/cool/ にアクセスしてテストしてください。

+ + + +

まとめ

+ +

これで、ローカルライブラリ用のスケルトン Web サイトプロジェクトを作成し、それが Node を使用して実行されることを確認しました。最も重要なのは、プロジェクトの構造も理解しているので、ローカルライブラリのルートとビューを追加するために変更を加える必要がある場所をよく理解していることです。

+ +

次に、図書館の Web サイトとして機能するようにスケルトンを変更します。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/server-side/express_nodejs/tutorial_local_library_website/index.html b/files/ja/learn/server-side/express_nodejs/tutorial_local_library_website/index.html new file mode 100644 index 0000000000..9e4dc43e1e --- /dev/null +++ b/files/ja/learn/server-side/express_nodejs/tutorial_local_library_website/index.html @@ -0,0 +1,98 @@ +--- +title: 'Express チュートリアル: 地域図書館の Web サイト' +slug: Learn/Server-side/Express_Nodejs/Tutorial_local_library_website +tags: + - Beginner + - CodingScripting + - Express + - Intro + - Learn + - Node + - Tutorial + - nodejs + - イントロダクション + - サーバサイド + - 初心者 + - 学習 +translation_of: Learn/Server-side/Express_Nodejs/Tutorial_local_library_website +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}}
+ +

実用的なチュートリアルシリーズの最初の記事では、これから学ぶことについて説明します。そして、私たちがこれから取り組んでいき、その後の記事で進化していく「地域図書館」のサンプルウェブサイトの概要を説明します。

+ + + + + + + + + + + + +
前提条件:Express のイントロダクションを読んでください。以下の記事では、Node 開発環境を設定する必要もあります。
目標:このチュートリアルで使用されているサンプルアプリケーションを紹介し、どのトピックがカバーされるのかを読者が理解できるようにします。
+ +

概要

+ +

MDN "Local Library" Express (Node) チュートリアルへようこそ。このチュートリアルでは、地域図書館の目録を管理するために使用される可能性があるウェブサイトを開発します。

+ +

この一連のチュートリアル記事では、次のことを行います。

+ + + +

あなたはすでにこれらのトピックのいくつかについて学び、他のものについても簡単に触れました。チュートリアルシリーズの最後までに、簡単な Express アプリケーションを自分で開発するのに十分な知識があるはずです。

+ +

LocalLibrary ウェブサイト

+ +

LocalLibrary は、この一連のチュートリアルの過程で作成および進化するウェブサイトの名前です。ご想像のとおり、この ウェブサイトの目的は利用できる書籍を閲覧したり自分のアカウントを管理したりできる、地元の小さな図書館用のオンライン目録を提供することです。

+ +

この例は慎重に選択されています。必要なだけ詳細を表示することも、ほとんどすべての Express 機能を見せるために使用することもできるためです。さらに重要なことに、それはどんなウェブサイトでも必要とする機能性へのガイド付きの道筋を提供することができます:

+ + + +

これは非常に拡張可能な例ですが、LocalLibrary と呼ばれる理由があります。私たちは Express をすぐに使い始めるための最小限の情報を示すことを望んでいます。その結果、私たちは本に関する情報、本のコピー、作者、その他の重要な情報を保存します。ただし、図書館が貸し出す可能性のある他のアイテムに関する情報を保存したり、複数の図書館サイトやその他の "大きな図書館" 機能をサポートするために必要なインフラストラクチャを提供することはしません。

+ +

私は立ち往生しています、どこでソースを入手できますか?

+ +

チュートリアルを進めていくうちに、適切なコードスニペットを各ポイントにコピーして貼り付けることができます。また、他のコードも (ある程度の手引きを付けて) 自身で拡張することを望んでいます。

+ +

すべてのコードスニペットをコピーして貼り付けるのではなく、それらを入力してみてください。次回同様のコードを書くときに、コードに慣れ親しんでいることになるので、長期的に役に立ちます。

+ +

あなたが立ち往生したならば、Github のここで ウェブサイトの完全に開発されたバージョンを見つけることができます。

+ +
+

メモ: この文書でテストされた Node、Express、および他のモジュールの特定のバージョンは、プロジェクト package.json にリストされています。

+
+ +

まとめ

+ +

LocalLIbrary ウェブサイトと、これから学ぶことについて少し知りました。今度は私たちの例を含むスケルトンプロジェクトの作成を開始します。

+ +

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}}

+ +

このモジュールの中

+ + diff --git a/files/ja/learn/server-side/first_steps/client-server_overview/index.html b/files/ja/learn/server-side/first_steps/client-server_overview/index.html new file mode 100644 index 0000000000..a99e34eff0 --- /dev/null +++ b/files/ja/learn/server-side/first_steps/client-server_overview/index.html @@ -0,0 +1,329 @@ +--- +title: クライアント - サーバの概要 +slug: Learn/Server-side/First_steps/Client-Server_overview +tags: + - Beginner + - CodingScripting + - イントロダクション + - ガイド + - サーバ + - サーバサイドプログラミング + - 学習 +translation_of: Learn/Server-side/First_steps/Client-Server_overview +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}
+ +

サーバサイドプログラミングの目的と潜在的な利点を知ったので、サーバがブラウザーから "動的リクエスト" を受け取ったときに起こることを詳細に検討します。ほとんどの Web サイトのサーバサイドコードは同様の方法でリクエストとレスポンスを処理するので、これは自身のコードの大部分を書くときに何が必要かを理解するのに役立ちます。

+ + + + + + + + + + + + +
前提条件基本的なコンピュータリテラシー。Web サーバの基本的な理解。
目的動的な Web サイトにおけるクライアントとサーバのやりとり、特にサーバサイドのコードで実行する必要がある操作を理解する。
+ +

私たちはまだコードを書くために使う Web フレームワークを選択していないので、議論には本当のコードはありません! ただし、どのプログラミング言語または Web フレームワークを選択したかに関係なく、記述された動作はサーバ側のコードで実装する必要があるため、この説明は依然として非常に重要です。

+ +

Web サーバと HTTP (入門書)

+ +

Web ブラウザは、HyperText Transfer Protocol (HTTP) を使用して Web サーバと通信します。Web ページのリンクをクリックしたり、フォームを送信したり、検索を実行したりすると、ブラウザは HTTP リクエストをサーバに送信します。

+ +

このリクエストに含まれるもの:

+ + + +

Web サーバはクライアントリクエストメッセージを待ち、メッセージが到着したときにそれらを処理し、HTTP レスポンスメッセージで Web ブラウザにレスポンスします。レスポンスには、リクエストが成功したかどうかを示す HTTP レスポンスステータスコード (例: 成功した場合は "200 OK"、リソースが見つからない場合は "404 Not Found"、ユーザがリソースを見ることを許可されていない場合は "403 Forbidden"など) が含まれます。GET リクエストに対する成功したレスポンスの本文には、リクエストされたリソースが含まれます。

+ +

HTML ページが返されると、Web ブラウザによってレンダリングされます。 処理の一部として、ブラウザは他のリソースへのリンクを発見することができ (例えば HTML ページは通常 JavaScript と CSS ページを参照します)、これらのファイルをダウンロードするために別々の HTTP リクエストを送信します。

+ +

静的および動的 Web サイト (以降のセクションで説明) は、まったく同じ通信プロトコル/パターンを使用しています。

+ +

GET リクエスト/レスポンス 例

+ +

リンクをクリックするか (検索エンジンホームページのように) サイトを検索することで簡単な GET リクエストをすることができます。たとえば、MDNで「クライアント - サーバの概要」という用語を検索したときに送信される HTTP リクエストは、次のようになります (メッセージの一部はあなたのブラウザや設定に依存するので、同一にはなりません)。

+ +
+

HTTP メッセージのフォーマットは「Web 標準」(RFC7230) で定義されています。このレベルの詳細を知る必要はありませんが、少なくとも今、これら全てがどこから来たのかを知っています!

+
+ +

リクエスト

+ +

リクエストの各行にはそれに関する情報が含まれています。 最初の部分はヘッダーと呼ばれ、HTML head に HTML 文書に関する有用な情報が含まれるのと同じで、リクエストに関する有用な情報が含まれます (ただし、実際のコンテンツ自体は含まれません)。

+ +
GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1
+Host: developer.mozilla.org
+Connection: keep-alive
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Referer: https://developer.mozilla.org/en-US/
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
+Accept-Language: en-US,en;q=0.8,es;q=0.6
+Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _gat=1; _ga=GA1.2.1688886003.1471911953; ffo=true
+
+ +

1行目と2行目には、上記で説明したほとんどの情報が含まれています。

+ + + +

最後の行にはクライアントサイドの Cookie に関する情報が含まれています。この場合、Cookie にはセッションを管理するための ID (Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; ...) が含まれています。

+ +

残りの行には、使用されているブラウザとそれが処理できるレスポンスの種類に関する情報が含まれています。たとえば、次のようになります。

+ + + +

HTTP リクエストもボディを持つことができますが、この場合は空です。

+ +

レスポンス

+ +

このリクエストに対するレスポンスの最初の部分を以下に示します。ヘッダーには、次のような情報が含まれています。

+ + + +

メッセージの最後に body があります。これにはリクエストによって返された実際の HTML が含まれています。

+ +
HTTP/1.1 200 OK
+Server: Apache
+X-Backend-Server: developer1.webapp.scl3.mozilla.com
+Vary: Accept,Cookie, Accept-Encoding
+Content-Type: text/html; charset=utf-8
+Date: Wed, 07 Sep 2016 00:11:31 GMT
+Keep-Alive: timeout=5, max=999
+Connection: Keep-Alive
+X-Frame-Options: DENY
+Allow: GET
+X-Cache-Info: caching
+Content-Length: 41823
+
+
+
+<!DOCTYPE html>
+<html lang="en-US" dir="ltr" class="redesign no-js"  data-ffo-opensanslight=false data-ffo-opensans=false >
+<head prefix="og: http://ogp.me/ns#">
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
+  <script>(function(d) { d.className = d.className.replace(/\bno-js/, ''); })(document.documentElement);</script>
+  ...
+ +

レスポンスヘッダの残りの部分には、レスポンス (たとえば、いつ生成されたか)、サーバ、およびブラウザがページを処理する方法についての情報が含まれます (たとえば、X-Frame-Options: DENY 行は、このページを別のサイトの {{htmlelement("iframe")}} に埋め込むことを許可しないようにブラウザに指示します)。

+ +

POST リクエスト/レスポンス 例

+ +

HTTP POST は、サーバに保存する情報を含むフォームを送信したときに作成されます。

+ +

リクエスト

+ +

以下のテキストは、ユーザがこのサイトに新しいプロファイルの詳細を送信したときに行われる HTTP リクエストを示しています。最初の行はこのリクエストを POST として識別しますが、リクエストのフォーマットは前述の GET リクエストの例とほぼ同じです。

+ +
POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1
+Host: developer.mozilla.org
+Connection: keep-alive
+Content-Length: 432
+Pragma: no-cache
+Cache-Control: no-cache
+Origin: https://developer.mozilla.org
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
+Content-Type: application/x-www-form-urlencoded
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Referer: https://developer.mozilla.org/en-US/profiles/hamishwillee/edit
+Accept-Encoding: gzip, deflate, br
+Accept-Language: en-US,en;q=0.8,es;q=0.6
+Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; _gat=1; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _ga=GA1.2.1688886003.1471911953; ffo=true
+
+csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwillee&user-fullname=Hamish+Willee&user-title=&user-organization=&user-location=Australia&user-locale=en-US&user-timezone=Australia%2FMelbourne&user-irc_nickname=&user-interests=&user-expertise=&user-twitter_url=&user-stackoverflow_url=&user-linkedin_url=&user-mozillians_url=&user-facebook_url=
+ +

主な違いは URL にパラメータがないことです。ご覧のとおり、フォームからの情報はリクエストの本文にエンコードされています (たとえば、新しいユーザーの fullname は &user-fullname=Hamish+Willee を使用して設定されます)。

+ +

レスポンス

+ +

リクエストからのレスポンスは以下のとおりです。ステータスコード "302 Found" は、投稿が成功したこと、および Location フィールドで指定されたページを読み込むために2番目の HTTP リクエストを発行する必要があることをブラウザに通知します。その他の点では、この情報は GET リクエストへのレスポンスに関する情報と似ています。

+ +
HTTP/1.1 302 FOUND
+Server: Apache
+X-Backend-Server: developer3.webapp.scl3.mozilla.com
+Vary: Cookie
+Vary: Accept-Encoding
+Content-Type: text/html; charset=utf-8
+Date: Wed, 07 Sep 2016 00:38:13 GMT
+Location: https://developer.mozilla.org/en-US/profiles/hamishwillee
+Keep-Alive: timeout=5, max=1000
+Connection: Keep-Alive
+X-Frame-Options: DENY
+X-Cache-Info: not cacheable; request wasn't a GET or HEAD
+Content-Length: 0
+
+ +
+

メモ: これらの例に示されている HTTP レスポンスとリクエストは Fiddler アプリケーションを使ってキャプチャされました、しかしあなたはウェブスニファ (例えば Websniffer) または HttpFox のようなブラウザ拡張を使って同様の情報を得ることができます。あなたはこれを自分で試すことができます。リンクされているツールを使用してから、サイト内を移動してプロファイル情報を編集し、さまざまなリクエストとレスポンスを確認します。最近のほとんどのブラウザには、ネットワーク要求を監視するツール (たとえば、Firefox のネットワークモニタツール) もあります。

+
+ +

静的サイト

+ +

静的サイトは、特定のリソースがリクエストされたときはいつでもサーバから同じハードコードされたコンテンツを返すものです。たとえば、製品に関するページが /static/myproduct1.html にある場合、この同じページがすべてのユーザに返されます。サイトに他の似たような製品を追加するならば、別のページ (例えば myproduct2.html) を追加する必要があるでしょう。これは本当に非効率的になり始めます — 何千もの製品ページにたどり着くとどうなるでしょう? 各ページ (基本的なページテンプレート、構造など) に渡って多くのコードを繰り返すことになります。ページ構造について何か変更したい場合 (たとえば、新しい「関連商品」セクションを追加するなど) 各ページを個別に変更する必要があります。

+ +
+

メモ: ページ数が少なく、すべてのユーザに同じコンテンツを送信したい場合は、静的サイトが優れています。ただし、ページ数が増えると、管理に多大なコストがかかる可能性があります。

+
+ +

前回の記事で見た静的サイトのアーキテクチャ図をもう一度見て、これがどのように機能するのかを要約しましょう。

+ +

A simplified diagram of a static web server.

+ +

ユーザがページにナビゲートしたい場合、ブラウザはその HTML ページの URL を指定した HTTP GET リクエストを送信します。サーバはファイルシステムからリクエストされた文書を検索し、その文書と "200 OK" の HTTP レスポンスステータスコード (成功を示す) を含む HTTP レスポンスを返します。ファイルがサーバに存在しない場合は "404 Not Found"、ファイルが存在するが別の場所にリダイレクトされている場合は "301 Moved Permanently" など、サーバは別のステータスコードを返すことがあります。

+ +

静的サイトのサーバは可変データを格納しないため、GET リクエストを処理するだけで済みます。また、HTTP リクエストデータ (URL パラメータやクッキーなど) に基づいてレスポンスを変更することもありません。

+ +

それでも、動的サイトは静的ファイル (CSS、JavaScript、静的イメージなど) に対するリクエストをまったく同じ方法で処理するため、静的サイトの機能を理解することはサーバーサイドプログラミングを学ぶときに役立ちます。

+ +

動的サイト

+ +

動的サイトとは、特定のリクエスト URL とデータに基づいてコンテンツを生成して返すことができる (特定の URL に対して常に同じハードコードファイルを返すのではない) サイトです。製品サイトの例を使用すると、サーバは個々の HTML ファイルではなくデータベースに製品の「データ」を保管します。商品の HTTP GET リクエストを受信すると、サーバは商品 ID を決定し、データベースからデータを取得してから、そのデータを HTML テンプレートに挿入することによって、レスポンス用の HTML ページを作成します。これは静的サイトよりも大きな利点があります。

+ +

データベースを使用すると、製品情報を簡単に拡張、変更、および検索可能な方法で効率的に格納できます。

+ +

HTML テンプレートを使用すると、HTML 構造の変更が非常に簡単になります。これは 1つの場所、1つのテンプレート内で行う必要があるだけで、潜在的に何千もの静的ページでは行わないためです。

+ +

動的リクエストの構造

+ +

このセクションでは、前回の記事で説明した内容に基づいて、「動的な」HTTP リクエストとレスポンスのサイクルの概要を段階的に説明します。"物事を現実のものにする" ために、コーチが HTML 形式で彼らのチーム名とチームサイズを選択して、彼らの次のゲームのために提案された "最高のラインナップ" を取り戻すことができるスポーツチームマネージャーウェブサイトのコンテキストを使います。

+ +

以下の図は、"チームコーチ" Web サイトの主な要素と、コーチが "ベストチーム"リストにアクセスしたときの一連の操作の番号付きラベルを示しています。サイトの一部は Web アプリケーション (これは HTTP リクエストを処理し、HTTP レスポンスを返すサーバーサイドコードを指す方法です)、データベース、これにはプレイヤー、チーム、コーチ、そして彼らの関係についての情報が含まれています、そして HTML テンプレートです。

+ +

This is a diagram of a simple web server with step numbers for each of step of the client-server interaction.

+ +

コーチがチーム名と選手の数を記入したフォームを送信した後、一連の操作は次のようになります。

+ +
    +
  1. Web ブラウザはリソースのベース URL (/best) を使用し、URL パラメータ (例: /best?team=my_team_name&show=11) または URL パターンの一部 (例: /best/my_team_name/11/) としてチームと選手の番号をエンコードしたものを使用して、サーバへの HTTP GET リクエストを作成します。 GET リクエストが使用されるのは、リクエストがデータをフェッチするだけである (データを変更しない) ためです。
  2. +
  3. Web サーバはリクエストが「動的」であることを検出し、それを処理 (Web サーバは、その構成で定義されたパターンマッチング規則に基づいてさまざまな URL を処理する方法を決定します) のために Web アプリケーションに転送します。
  4. +
  5. Web アプリケーションは、リクエストの意図が URL (/best/) に基づいて「最善のチームリスト」を取得することであることを識別し、URL から必要なチーム名とプレーヤーの数を見つけます。その後、Web アプリケーションはデータベースから必要な情報を取得します (どのプレーヤーが「最高」であるかを定義するために追加の「内部」パラメータを使用し、クライアント側の Cookie からログインしたコーチの身元も取得する可能性があります)。
  6. +
  7. Web アプリケーションは、(データベースからの) データを HTML テンプレート内のプレースホルダに入れることで、HTML ページを動的に作成します。
  8. +
  9. Web アプリケーションは、生成された HTML を HTTP ステータスコード 200 ("成功") とともに (Web サーバ経由で) Web ブラウザに返します。HTML が返されるのを妨げるものがあると、Web アプリケーションは別のコードを返します。たとえば、"404" はチームが存在しないことを示します。
  10. +
  11. Web ブラウザは返された HTML の処理を開始し、それが参照する他の CSS または JavaScript ファイルを取得するための個別のリクエストを送信します (手順7を参照)。
  12. +
  13. Web サーバはファイルシステムから静的ファイルをロードして直接ブラウザに返します (これも正しいファイル処理は設定ルールと URL パターンマッチングに基づいています)。
  14. +
+ +

データベース内のレコードを更新する操作も同様に処理されます。データベースの更新と同じですが、ブラウザからの HTTP リクエストは POST リクエストとしてエンコードされるはずです。

+ +

他の仕事をする

+ +

Web アプリケーションの仕事は、HTTP リクエストを受信し、HTTP レスポンスを返すことです。情報を取得または更新するためにデータベースと相互にやり取りすることは非常に一般的な作業ですが、コードは同時に他のことを実行することも、データベースとやり取りしないこともあります。

+ +

Web アプリケーションが実行する追加のタスクの良い例は、ユーザにメールを送信してサイトへの登録を確認することです。サイトはロギングやその他の操作も実行する可能性があります。

+ +

HTML 以外のものを返す

+ +

サーバサイドの Web サイトのコードは、レスポンスに HTML スニペット/ファイルを返す必要はありません。代わりに動的に他の種類のファイル (テキスト、PDF、CSV など) あるいはデータ (JSON、XML など) を作成して返すことができます。

+ +

データを動的に更新して自身のコンテンツ ({{glossary("AJAX")}}) を更新できるようにするために Web ブラウザにデータを返すという考えはかなり前からありました。ごく最近では、必要に応じて動的に更新される単一の HTML ファイルを使用して Web サイト全体が作成される「シングルページアプリケーション」が普及しています。このスタイルのアプリケーションを使用して作成された Web サイトは、サーバから Web ブラウザに多くの計算コストをかけます。その結果、Web サイトは (応答性が高いなど) ネイティブアプリケーションのように動作するように見えます。

+ +

Web フレームワークはサーバサイドの Web プログラミングを簡素化します

+ +

サーバサイド Web フレームワークにより、上記の操作を処理するコードを非常に簡単に書くことができます。

+ +

それらが実行する最も重要な操作の1つは、異なるリソース/ページの URL を特定のハンドラ関数にマッピングするための単純なメカニズムを提供することです。これにより、各タイプのリソースに関連付けられているコードを別々にしておくことが容易になります。また、ハンドラ機能を変更することなく、特定の機能を提供するために使用される URL を1か所で変更できるため、メンテナンスの面でもメリットがあります。

+ +

たとえば、2つの URL パターンを2つのビュー関数にマップする次の Django (Python) コードを考えてみましょう。最初のパターンは、/best のリソース URL を持つ HTTP リクエストが views モジュールの index() という名前の関数に渡されることを保証します。パターン "/best/junior" を持つリクエストは、代わりに junior() ビュー関数に渡されます。

+ +
# file: best/urls.py
+#
+
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+    # example: /best/
+    url(r'^$', views.index),
+    # example: /best/junior/
+    url(r'^junior/$', views.junior),
+]
+ +
+

メモ: url() 関数の最初のパラメータは、「正規表現」(RegEx, または RE) と呼ばれるパターンマッチング手法を使用するため、少し奇妙に見える (たとえば、r'^junior/$') ことがあります。この時点で正規表現がどのように機能するのかを知る必要はありませんが、(上のハードコードされた値ではなく) URL のパターンと一致させてビュー関数のパラメータとして使用できるという点が異なります。例として、非常に単純な RegExは、「1つの大文字に一致し、その後に4〜7つの小文字を一致させる」と言うかもしれません。

+
+ +

Web フレームワークはまた、ビュー関数がデータベースから情報を取得するのを容易にします。データの構造はモデルで定義されています。これは基礎となるデータベースに格納されるフィールドを定義する Python クラスです。"team_type" のフィールドを持つ Team というモデルがある場合は、単純なクエリ構文を使用して特定のタイプを持つすべてのチームを取り戻すことができます。

+ +

以下の例では、 "大文字と小文字を区別する" 正確な team_type が "junior" であるすべてのチームのリストを取得します — フォーマットに注意してください。フィールド名 (team_type) の後に二重下線、そして使用する一致のタイプ (この場合は exact) です。他にもたくさんの種類の一致があり、それらをデイジーチェーンでつなげることができます。 順序と返される結果の数も制御できます。

+ +
#best/views.py
+
+from django.shortcuts import render
+
+from .models import Team
+
+
+def junior(request):
+    list_teams = Team.objects.filter(team_type__exact="junior")
+    context = {'list': list_teams}
+    return render(request, 'best/index.html', context)
+
+ +

junior() 関数は、ジュニアチームのリストを取得した後、render() 関数を呼び出して、元の HttpRequest、HTML テンプレート、およびテンプレートに含める情報を定義する  "context"  オブジェクトを渡します。render() 関数は、コンテキストと HTML テンプレートを使用して HTML を生成し、それを HttpResponse オブジェクトに返す便利な関数です。

+ +

明らかに Web フレームワークは他にも多くのタスクで役に立ちます。次の記事では、さらに多くの利点といくつかの一般的な Web フレームワークの選択について説明します。

+ +

まとめ

+ +

この時点で、サーバサイドコードが実行しなければならない操作の概要を把握し、サーバーサイド Web フレームワークがこれを容易にする方法をいくつか知っているはずです。

+ +

次のモジュールでは、最初のサイトに最適な Web フレームワークを選択する手助けをします。

+ +

{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}

+ +

このモジュールの記事一覧

+ + diff --git a/files/ja/learn/server-side/first_steps/index.html b/files/ja/learn/server-side/first_steps/index.html new file mode 100644 index 0000000000..d5acb8040f --- /dev/null +++ b/files/ja/learn/server-side/first_steps/index.html @@ -0,0 +1,47 @@ +--- +title: サーバサイドの Web サイトプログラミングの第一歩 +slug: Learn/Server-side/First_steps +tags: + - CodingScripting + - Intro + - Landing + - ガイド + - サーバサイドプログラミング + - 初心者 + - 学習 +translation_of: Learn/Server-side/First_steps +--- +
{{LearnSidebar}}
+ +

このモジュールでは、サーバーサイドプログラミングに関するいくつかの基本的な質問、"これは何?"、"クライアントサイドプログラミングとどう違うの?"、"なぜ便利なの?" について答えます。次に、最も人気のあるサーバーサイドの ウェブフレームワークの概要と、最初のサイトを作成するための最適なフレームワークの選択方法に関するガイダンスを提供します。最後に、ウェブサーバーのセキュリティに関する高度な入門記事を提供します。

+ +

前提条件

+ +

このモジュールを開始する前に、サーバーサイドのウェブサイトプログラミングやその他のプログラミングの知識は必要ありません。

+ +

しかしながら、"ウェブがどのように動作するか" を理解する必要があります。まず以下のトピックスを読むことをお勧めします:

+ + + +

基本的な事を理解できれば、このセクションを読み進む準備が整います。

+ +

ガイド

+ +
+
サーバーサイドの概要
+
MDN のサーバーサイドプログラミング入門コースにようこそ!この最初の記事では、「それはどんなもの?」、「クライアントサイドプログラミングとはどう違う?」、「なぜ便利なの?」 という質問に答えながら、ハイレベルな視点からサーバーサイドプログラミングを見ていきます。この記事を読めば、サーバーサイドコーディングを行うことで、 ウェブサイトにどんな機能を加えることができるか、理解できるようになります。
+
クライアント-サーバーの概要
+
サーバーサイドプログラミングの目的と潜在的な利点を知ったので、サーバーがブラウザーから "動的リクエスト" を受け取ったときに起こることを詳細に検討します。ほとんどのウェブサイトのサーバーサイドコードは同様の方法でリクエストとレスポンスを処理するので、これは自身のコードの大部分を書くときに何が必要かを理解するのに役立ちます
+
サーバーサイドウェブフレームワーク
+
前の記事では、ウェブクライアントとサーバー間の通信、HTTP リクエストとレスポンスの性質、およびウェブブラウザーからのリクエストにレスポンスするためにサーバーサイドウェブアプリケーションが実行する必要があることについて説明しました。この知識をもとに、ウェブフレームワークがどのようにこれらのタスクを単純化できるかを探り、最初のサーバーサイドウェブアプリケーションのためのフレームワークをどのように選択するかを考えてみましょう。
+
ウェブサイトのセキュリティ
+
ウェブサイトのセキュリティでは、ウェブサイトのデザインと使用方法のあらゆる面で警戒が必要です。この入門記事ではウェブサイトのセキュリティの第一人者にはなりませんが、脅威がどこから発生するのか、そして最も一般的な攻撃に対して Web アプリケーションを強化するために何ができるのかを理解するのに役立ちます。
+
+ +

課題

+ +

コードをまだ説明していないため、この "概要" モジュールでは課題はありません。サーバーサイドプログラミングを使用して提供できる機能の種類が何かをよく理解していることを期待しています。また、初めてのウェブサイトを作成するために使用するサーバーサイドの ウェブフレームワークについて決断していることを望みます。

diff --git a/files/ja/learn/server-side/first_steps/introduction/index.html b/files/ja/learn/server-side/first_steps/introduction/index.html new file mode 100644 index 0000000000..5c269c2d6a --- /dev/null +++ b/files/ja/learn/server-side/first_steps/introduction/index.html @@ -0,0 +1,227 @@ +--- +title: サーバサイドの概要 +slug: Learn/Server-side/First_steps/Introduction +translation_of: Learn/Server-side/First_steps/Introduction +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}
+ +

MDN のサーバサイドプログラミング入門コースにようこそ!この最初の記事では、「それはどんなもの?」、「クライアントサイドプログラミングとはどう違う?」、「なぜ便利なの?」 という質問に答えながら、ハイレベルな視点からサーバサイドプログラミングを見ていきます。この記事を読めば、サーバサイドコーディングを行うことで、 Web サイトにどんな機能を加えることができるか、理解できるようになります。

+ + + + + + + + + + + + +
学習の前提:基本的なコンピュータリテラシーと、Webサーバーとはどんなものかという基礎知識を有していること
学習の目的:サーバーサイドプログラミングとは何か、何ができるのか、クライアントサイドプログラミングとどこが違うのか、といった点を理解すること
+ +

大規模なウエブサイトの多くは、サーバーサイドコードを使うことで、必要に応じた、異なるデータを動的に表示しています。このデータは、サーバーのデータベースから取り出され、クライアントに送られると、(HTMLやJavaScriptで記述された)クライアントサイドコードで表示されます。 

+ +

サーバーサイドコードを使う最大の利点は、個々のユーザーにあわせて、ウエブサイトのコンテンツ調整できることでしょう。動的サイトでは、ユーザーの好みや傾向をもとに、より適切なコンテンツを強調表示することができます。また、ユーザーの好みや情報を取り込んで利用することにより、サイトを使いやすくすることもできます。例えば、クレジットカード情報を保管して、次の支払いが簡単に済むようにできます。

+ +

加えて、通知や更新情報などを電子メールなどで送ることで、サイトユーザーとの更なるやりとりが可能になります。このような機能を活用することで、ユーザーとの繋がりをより強固なものにできるのです。.

+ +

現代のウエブ開発では、サーバーサイドプログラミングを習得することが求められるようになってきました。.

+ +

サーバーサイドプログラミングとは何か?

+ +

Webブラウザは、「ハイパー・テキスト・トランスファー・プロトコル ({{glossary("HTTP")}})」を使ってWebサーバーと通信します。Webページのリンクをクリックしたり、入力フォームを送信したり、検索を実行したりすると、ブラウザからサーバーへHTTP要求(Request)が送信されます。

+ +

この要求には、関係するデータを指定するURLや、要求するアクションを定めるメソッド(データの取得、削除、送付など)が含まれます。さらに追加情報を含むこともあります。たとえば(HTTP POSTメソッドを使ったデータ送付のように)URLのパラメータとしてクエリー文字列を付加したり、{{glossary("Cookie", "クッキー")}}を使ったりします。

+ +

Webサーバーはクライアントからの要求を待ちうけ、受信したら、処理を行ってからHTTP応答(Response)メッセージをブラウザに返します。このとき応答には、要求の実行が成功したか否かの結果(例えば成功したら"HTTP/1.1 200 OK")を含めます。 

+ +

成功したときの応答には、要求されたデータ(例えば、新しいHTMLページやが画像など)が含まれ、ブラウザ上に表示されることになります。

+ +

静的サイト

+ +

下図に静的サイトとなるWebサーバーの動作を示します。静的サイトは、要求されたデータに対して、常に一定のコンテンツを返します。新しいページに移るときには、ブラウザから、そのURLを指定したHTTP GET(取得)要求を送ります。

+ +

サーバーは要求された文書をファイルシステムから取り出し、成功ステータス(通常は"200 OK")と一緒にHTTPレスポンスに入れて送り返します。何らかの原因でファイルが取り出せないときは、エラーステータス(クライアント側エラーあるいはサーバー側エラー)を送り返します。

+ +

A simplified diagram of a static web server.

+ +

動的サイト

+ +

動的ウエブサイトでは、応答に含まれるコンテンツの一部が、必要に応じて「動的に」生成されます。多くの場合、動的ウエブサイトのHTMLページは、データベースから取り出したデータを、HTML雛型の指定された場所に埋め込むことで作られます。これは大量のコンテンツを保存するのに、静的ウエブサイトと比べて、はるかに効率的な方法と言えます。 

+ +

動的サイトは、ユーザーから与えられた情報や保存された好みに応じて、同じURL要求であっても異なるデータを返すことができます。また、応答を返すときに他の操作(例えば通知をするなど)を行うこともできます。

+ +

動的ウエブサイトを実現するコードの大部分は、サーバー上で実行されます。このコードを作ることを、「サーバーサイドプログラミング」(あるいは「バックエンドスクリプティング」)と言います。

+ +

比較的簡単な動的ウエブサイトの動作を下図に示します。ブラウザがHTTP要求を送ると、サーバーがその要求を処理して、HTTP応答を返すところは、静的ウエブサイトの場合と同じです。

+ +

静的データを要求されたときは、静的サイトと同じ動作をします。静的データはファイルとして保存されており、変更されることはありません。例えば、CSS、JavaScript、画像、事前に作成されたPDFファイルなどが、これにあたります。 

+ +

A simplified diagram of a web server that uses server-side programming to get information from a database and construct HTML from templates. This is the same diagram as is in the Client-Server overview.

+ +

動的データを要求されたときは、②の矢印が示すように、(図では「Webアプリケーション」と表示されている)他のサーバーサイドコードに転送されます。そこで要求を解釈し、必要なデータをデータベースから取り出し(③)、HTML雛型に埋め込みます(④)。それを応答としてブラウザに送り返します(⑤と⑥)。 

+ +
+

サーバーサイドプログラミングとクライアントサイドプログラミングは同じものか?

+
+ +

サーバーサイドプログラミングとクライアントサイドプログラミングを比較してみましょう。両者には明確な差異があります。

+ + + +

ブラウザで走るコードはクライアントサイドコードと呼ばれ、主要な課題は表示されるページの外観や動作を実現することにあります。例えば、どんなユーザーインターフェースを選んでまとめるか、どう配置するか、ナビゲーションをどう支援するか、フォームの入力をどう検証するかといった検討が重要です。いっぽうサーバーサイドプログラミングで重視されるのは、要求に合わせて返すコンテンツをどう選択するかということです。サーバーサイドコードが扱うのは、送付されてきたデータと要求を整合させること、データベースへのデータ登録と取り出し、要求に合致したデータを送り返すことなどです。

+ +

クライアントサイドコードに使う言語は、HTMLCSS、それにJavaScript です。コードはブラウザ内で実行され、OSへのアクセスはないか、あってもわずかです。ファイルシステムへのアクセスも限定的です

+ +

ユーザーが使うブラウザを、Web開発者が選ぶことはできません。クライアントサイドコードを実行するとき、ブラウザの互換性が問題になることがあります。実のところ、ブラウザ間の互換性の差異を克服することが、クライアントサイドプログラミングの大きな課題になっています。

+ +

サーバーサイドコードを書くのには、さまざまな言語が使えます。主な例をあげると、PHP、 Python、 Ruby、 C#、NodeJS(JavaScript)などがあります。サーバーサイドコードはサーバーのOSを全面的に利用します。開発者は自分の用途に最適な言語(さらにはバージョンまで)を選ぶことになります。

+ +

コードの開発には、Webフレームワーク がよく使われます。フレームワークとは、関数やオブジェクト、規則やコード構造などを集めたものです。よく出会う問題を解決したり、開発期間を短縮したり、ある分野で直面する様々な課題を単純化するのに役立てることができます。.

+ +

フレームワークは、クライアントサイドコードでもサーバーサイドコードで使われます。しかし、くり返しになりますが、両者には明確な差があり、フレームワークも同じものではありえません。クライアントサイドWebフレームワークが配置と表現力を改善するだけなのに対し、サーバーサイドWebフレームワークはWebサーバーに共通する機能を提供しています。それを使わないとすると、多くの機能を自分で実装する必要があります。例えば、対話セッション、ユーザー認証、データベースへのアクセス、ライブラリの雛型化はフレームワークで実現できます。

+ +
+

:クライアントサイドフレームワークは、クライアントサイドコードの開発を加速するのによく使われます。いっぽう、すべてのコードを手作りすることも可能です。実際のところ、単純なユーザーインターフェースしか必要としない小規模なWebサイトであれば、手作りの方が短期間で効率的な開発ができます。

+ +

これに反して、Webアプリのサーバーサイドコンポーネントの開発は、フレームワークなしでは考えられません。HTTPサーバーのように重要な機能を最初から、例えばPythonで記述するのは、非常に困難なことです。ところがDjangoのようのPython Webフレームワークなら、すぐに使えるし、多くの有用なツールも提供されます。

+
+ +
+

サーバーサイドではどんなことができるのか?

+ +

サーバーサイドプログラミングが役に立つのは、ユーザーごとに仕立てられた情報を、効率的に提供できるからです。またそのおかげで、ユーザーにとって使い勝手がよくなるからです。

+
+ +

例えばアマゾンのような会社では、サーバーサイドプログラミングによって、商品を検索したり、顧客の好みや過去の購入履歴から商品を勧めたり、すぐに購入できるようにしたりすることができます。

+ +

銀行でのサーバーサイドプログラミングでは、口座情報を保管し、それを見たり送金したりするのは、認証の済んだ顧客だけができるようにしています。外にも、Facebook、Twitter、InstagramあるいはWikipdeiaでは、サーバーサイドプログラミングを使って、興味あるコンテンツを取り上げて公開するときのアクセス制限を行っています。

+ +

サーバーサイドプログラミングの主な用途とその利点を以下にまとめます。一部は重複しているのが分かると思います。

+ +

情報を効率的に保管し、提供する

+ +

アマゾンには何点の商品があるのか、考えたことはありますか? Facebookへの投稿件数はどうでしょう? それぞれの商品や投稿ごとに静的なページを作るのは、まったく不可能なことです。

+ +

それに代わって、サーバーサイドプログラミングでは、データベースにデータを格納し、動的にページを構成して、HTMLや他の形式(例えばPDFや画像など)のファイルを送り返します。あるいは、{{glossary("JSON")}}や {{glossary("XML")}}などのファイルを送り返すだけで、クライアントサイドの適切なフレームワークに処理を任せることもできます。こうするとサーバーの負荷を軽減し、送るデータ量を削減することができます。

+ +

サーバーの送り返す情報は、データベースから取り出したものだけに限りません。他のソフトウエアツールの処理結果だったり、通信サービスが受信したものでも構いません。さらに言うと、ユーザーが使っている機器に合わせて、コンテンツを調整することもできます。

+ +

上方がデータベースに保管されているので、他のビジネスシステムが読み取ったり、変更することも可能です。例えば、商品が店舗で売れようと、オンラインで売れようと、一括して在庫を管理できるようになります。

+ +
+

実例:以下の例を見れば、サーバーサイドコードが、情報の効率的な保管と提供に役立つことが実感できるでしょう。

+ +
    +
  1. アマゾンなどの通販サイトを見てください。
  2. +
  3. キーワードを与えて商品を検索します。検索結果はさまざまですが、結果のページの構造は一定です。 
  4. +
  5. 検索結果から各商品のページをを比較してみましょう。ページの構造や配置は全く同じことが分かります。商品ごとの情報をデータベースから取り出して、埋め込んでいるからです。
  6. +
+ +

「魚」といった一般的な検索語を使うと、それこそ何百万件もの結果が出てきます。データベースを使うことで、これだけの情報を保管して提供できるのです。ただし、その情報は常に同じ場所に埋め込まれるのです。

+
+ +

ユーザーごとに使い勝手を改善する

+ +

サーバーが保管しているクライアントの情報を活用すれば、ユーザーにとって便利なものになり、使い勝手が向上します。例えば、多くの販売サイトではクレジットカード情報を保管して、再入力の手間がかからないようにしています。Googleマップのようなサイトでは、ユーザーの現在位置や保管していた位置を使って、経路情報を提供したり、検索や実際の旅行記録から現地のお勧め場所を表示したりします。

+ +

ユーザーの関心を良く分析することで、どこに行きたいかを予想し、それに合わせた応答や通知ができるのです。例えば、以前に行った場所とか、人気の場所を地図上に表示します。

+ +
+

実例:Googleマップ はユーザーの検索や移動の履歴を保管しています。よく行く場所や検索が多い場所を、優先して表示しています。

+ +

Google検索の結果は、以前の検索履歴によって優先づけられています。

+ +
    +
  1.  Google検索のページを開いてください。
  2. +
  3.  まず「サッカー」を検索します。
  4. +
  5.  今度は、検索語欄に「好きな」と入力して、そのあとに続く語句の候補を見てください。
  6. +
+ +

偶然だろうって? とんでもない!

+
+ +

コンテンツへのアクセスを制限する

+ +

サーバーサイドプログラミングでは、認証ユーザーのデータへのアクセスを制限し、別のユーザーには許可された情報のみを表示します。

+ +

こんな実例があります:

+ + + +
+

実例:コンテンツへのアクセスを制限している実例を見てみましょう。銀行のオンラインサイトにアクセスすると、何が見えますか? あなたの口座にログインすると、どんな情報が出てきますか? どれが変更できますか? 銀行側にしか変更できない情報は何でしょう?

+
+ +

セッションや途中状態の情報を保管する

+ +

サーバーサイドプログラミングでは、「セッション」を実装できます。これは、現在のサイトユーザーの情報を保管して、それに基づいた応答を返すようにしたものです。

+ +

この機能により、ユーザーのログイン履歴を知ることで、電子メールや過去に購入した商品へのリンクを表示できます。オンラインゲームであれば、途中結果を保存して、そこから再開できるできるようになります。

+ +
+

実例:購読型の新聞サイト(例えばThe Ageなど)を開いて、無料で記事を読んでみてください。数時間か数日読み続けると、購読について説明するページに誘導され、それ以上記事が読めなくなります。これはクッキーで保管されたセッション情報を活用する一例です。(訳注:日本の新聞サイトでは、最初に無料登録を求められますが、そのあとは同じです。)

+
+ +

通知やメッセージを送る

+ +

サーバーは一般的なお知らせやユーザー毎のお知らせをすることができます。ウエブサイトで表示するだけでなく、電子メールやSNS、インスタントメッセージ、ビデオ会議など、さまざまな通信を使っています。

+ +

例をあげてみましょう:

+ + + +
+

実例:: いちばんよくある通知の例は、登録確認のお知らせでしょう。興味のある大規模サイト(Google、アマゾンや Instagramなど)ならどこでもいいので、電子メールアドレスを使ってアカウント作ってみてください。すぐに電子メールが届いて、登録完了を知らせてくるか、登録を確認してアカウントを有効にするよう求められます。

+
+ +

データを分析する

+ +

ウエブサイトはユーザーに関する大量のデータを収集します。何を検索したか、どんな商品を購入したか、何を推奨したか、そのページにどれだけ長く留まっていたかなどです。サーバーサイドプログラミングでは、このデータを分析することで、応答を改善できるのです。

+ +

例えばアマゾンやGoogleは、どちらも以前の検索(や購入)をもとに、商品の宣伝を表示します。

+ +
+

実例: Facebookユーザーの方は、自分のフィードにある投稿を見てください。 記事が、時間順になっていないときがあります。新しい投稿より、「いいね!」が多いもののほうがリストの前の方に現れたりします。

+ +

サイトに表示される広告を見てください。その中には、他のサイトで閲覧したものがあります。Facebookがどんなアルゴリズムでコンテンツや広告を処理しているのか不明な点もありますが、あなたの「いいね!」や閲覧履歴をもとにしていることだけは確かです。

+
+ +

まとめ

+ +

サーバーサイドプログラミングの最初の記事はこれで終わりです。お疲れさまでした。 

+ +

ここまでで、サーバーサイドコードはWebサーバー上で実行され、ユーザーにどんな情報を送るか決めるのが主な目的であることを学びました。(ちなみにクライアントサイドコードは、そのデータを配置したり表現したりするのが目的です。)

+ +

その有用性は、ウエブサイトが個々のユーザーに合わせた情報を効率的に提供できることと、サーバーサイドで実現できる機能について様々なアイディアが提供できることにあることも、理解いただけたでしょう。

+ +

それから、サーバーサイドコードを書く言語は何種類もあり、Webフレームワークを使えば簡単に作れることも、お分かりいただけましたでしょうか。 

+ +

次からの記事では、どのフレームワークを選べばよいか考えます。それから、クライアント・サーバー間の相互作用について、もう少し詳しく説明します。

+ +

{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}

+ +

このモジュールの記事一覧

+ + diff --git a/files/ja/learn/server-side/first_steps/web_frameworks/index.html b/files/ja/learn/server-side/first_steps/web_frameworks/index.html new file mode 100644 index 0000000000..5d26d30fa1 --- /dev/null +++ b/files/ja/learn/server-side/first_steps/web_frameworks/index.html @@ -0,0 +1,328 @@ +--- +title: サーバーサイドウェブフレームワーク +slug: Learn/Server-side/First_steps/Web_frameworks +tags: + - Beginner + - CodingScripting + - Guide + - Intro + - Learn + - Server + - Server-side programming + - Web frameworks +translation_of: Learn/Server-side/First_steps/Web_frameworks +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}
+ +

前の記事では、ウェブクライアントとサーバー間の通信、HTTP リクエストとレスポンスの性質、およびウェブブラウザーからのリクエストにレスポンスするためにサーバーサイドウェブアプリケーションが実行する必要があることについて説明しました。この知識をもとに、ウェブフレームワークがどのようにこれらのタスクを単純化できるかを探り、最初のサーバーサイドウェブアプリケーションのためのフレームワークをどのように選択するかを考えてみましょう。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシー。サーバー側のコードがHTTP リクエストを処理しレスポンスする方法 (クライアント - サーバーの概要を参照) についての基本的な知識。
目的:ウェブフレームワークがサーバーサイドコードの開発/メンテナンスをどのように簡略化できるかを理解し、自分たちの開発のためのフレームワークの選択を考えさせるため。
+ +

次のセクションでは、実際のウェブフレームワークから取得したコードフラグメントを使用していくつかのポイントを説明します。今ではすべてが意味をなさない場合でも、心配しないでください、フレームワーク固有のモジュールコードを通して作業していきますので。

+ +

概要

+ +

サーバーサイドウェブフレームワーク (別名「ウェブアプリケーションフレームワーク」)は、ウェブアプリケーションの作成、保守、および拡張を容易にするソフトウェアフレームワークです。適切なハンドラーへの URL のルーティング、データベースとのやり取り、セッションとユーザー認証のサポート、出力のフォーマット (HTML、JSON、XML など)、ウェブ攻撃に対するセキュリティの向上など、一般的なウェブ開発タスクを簡素化するツールとライブラリーを提供します。

+ +

次のセクションでは、ウェブフレームワークによってウェブアプリケーションの開発がどのように容易になるかについて、もう少し詳しく説明します。次に、ウェブフレームワークを選択するために使用できる基準のいくつかを説明し、次にいくつかの選択肢をリストします。

+ +

ウェブフレームワークで何ができますか?

+ +

ウェブフレームワークは、一般的なウェブ開発操作を簡素化するためのツールとライブラリーを提供します。サーバーサイドのウェブフレームワークを使う必要はありませんが、使用することを強くお勧めします。それはあなたの人生をずっと楽にするでしょう。

+ +

このセクションでは、ウェブフレームワークによって提供されることが多い機能の一部について説明します (必ずしもすべてのフレームワークがこれらの機能のすべてを提供するわけではありません!)。

+ +

HTTP リクエストとレスポンスを直接操作する

+ +

前回の記事で見たように、ウェブサーバーとブラウザーは HTTP プロトコルを介して通信します。サーバーはブラウザーからの HTTP リクエストを待ってから HTTP レスポンスで情報を返します。ウェブフレームワークを使用すると、これらのリクエストとレスポンスを処理するためのサーバーサイドコードを生成する単純化された構文を作成できます。これは、低レベルのネットワークプリミティブよりも、より簡単な、より高レベルのコードと対話する、より簡単な仕事を持つことを意味します。

+ +

以下の例は、Django (Python) ウェブフレームワークでどのように機能するかを示しています。すべての "view" 関数 (リクエストハンドラー) はリクエスト情報を含む HttpRequest オブジェクトを受け取り、フォーマットされた出力 (この場合は文字列) と共に HttpResponse オブジェクトを返す必要があります。

+ +
# Django view function
+from django.http import HttpResponse
+
+def index(request):
+    # Get an HttpRequest (request)
+    # perform operations using information from the request.
+    # Return HttpResponse
+    return HttpResponse('Output string to return')
+
+ +

適切なハンドラーにリクエストをルーティングする

+ +

ほとんどのサイトは、別々の URL からアクセス可能なさまざまなリソースを数多く提供します。これらすべてを 1 つの関数で処理するのは管理が難しいため、ウェブフレームワークは URL パターンを特定のハンドラー関数にマッピングするための単純なメカニズムを提供します。ベースとなるコードを変更することなく特定の機能を提供するために使用される URL を変更できるため、このアプローチにはメンテナンスの面でも利点があります。

+ +

フレームワークが異なると、マッピングに異なるメカニズムを使用します。たとえば、Flask (Python) ウェブフレームワークは、デコレーターを使用してビュー関数への経路を追加します。

+ +
@app.route("/")
+def hello():
+    return "Hello World!"
+ +

Django は開発者が URLパターンとビュー関数の間の URLマッピングのリストを定義することを期待しています。

+ +
urlpatterns = [
+    url(r'^$', views.index),
+    # example: /best/myteamname/5/
+    url(r'^(?P<team_name>\w.+?)/(?P<team_number>[0-9]+)/$', views.best),
+]
+
+ +

リクエスト内のデータに簡単にアクセスできるようにする

+ +

データはさまざまな方法で HTTP リクエストにエンコードできます。サーバーからファイルまたはデータを取得するためのHTTP GET リクエストは、URL パラメータまたは URL 構造内で必要なデータをエンコードすることができます。サーバー上のリソースを更新するための HTTP POST リクエストは、代わりに更新情報をリクエストの本文内に「POST データ」として含めます。HTTP リクエストはまた、現在のセッションまたはユーザーに関する情報をクライアント側のクッキーに含めることができます。

+ +

ウェブフレームワークは、この情報にアクセスするためのプログラミング言語に適したメカニズムを提供します。
+ たとえば、Django がすべてのビュー関数に渡す HttpRequest オブジェクトには、対象の URL にアクセスするためのメソッドとプロパティ、リクエストの種類 (HTTP GET など)、GET または POST パラメーター、cookie、セッションデータなどが含まれます。Django は URL マッパーで「キャプチャパターン」を定義することで URL の構造にエンコードされた情報を渡すこともできます (上のセクションの最後のコードを見てください)。

+ +

データベースアクセスを抽象化および単純化する

+ +

ウェブサイトは、データベースを使用して、ユーザと共有するための情報とユーザに関する情報の両方を保存します。ウェブフレームワークは多くの場合、データベースの読み取り、書き込み、照会、および削除操作を抽象化するデータベース層を提供します。この抽象化層は、オブジェクトリレーショナルマッパー (ORM) と呼ばれます。

+ +

ORM を使用することには、2 つのメリットがあります。

+ + + +

たとえば、Django のウェブフレームワークは ORM を提供し、レコードの構造をモデルとして定義するために使用されるオブジェクトを参照します。モデルは格納されるべきフィールドタイプを特定し、それはどの情報を格納することができるかについてのフィールドレベルの検証 (例えば、メールフィールドは有効なメールアドレスのみを許可する) を提供できます。フィールド定義では、最大サイズ、デフォルト値、選択リストのオプション、ドキュメントのヘルプテキスト、フォームのラベルテキストなども指定できます。コードとは別に変更されるかもしれない設定であるため、モデルは基礎となるデータベースについてのいかなる情報も述べません。

+ +

以下の最初のコードスニペットは、Team オブジェクト用の非常に単純な Django モデルを示しています。これは、チーム名とチームレベルを文字フィールドとして格納し、各レコードに格納する最大文字数を指定します。team_level は選択項目なので、表示される選択項目と保管されるデータの間のマッピング、およびデフォルト値も提供します。

+ +
#best/models.py
+
+from django.db import models
+
+class Team(models.Model):
+    team_name = models.CharField(max_length=40)
+
+    TEAM_LEVELS = (
+        ('U09', 'Under 09s'),
+        ('U10', 'Under 10s'),
+        ('U11, 'Under 11s'),
+        ...  #list our other teams
+    )
+    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
+
+ +

Django モデルはデータベースを検索するための簡単なクエリ API を提供します。これは、異なる基準 (正確、大文字と小文字を区別しない、より大きいなど) を使用して一度に多数のフィールドに対して一致させることができ、複雑なステートメント (たとえば、"Fr" で始まるチーム名、または "al" で終わるチーム名を持つ U11 チームの検索を指定できます) をサポートすることができます。

+ +

2番目のコードスニペットは、すべての U09 チームを表示するための表示機能 (リソースハンドラー) を示しています。この場合、team_level フィールドのテキストが 'U09' であるすべてのレコード (この基準が、2つの下線で区切られたフィールド名と一致タイプを持つ引数、つまり team_level__exact として filter() 関数に渡されることに注意してください) をフィルタリングしたいと指定します。

+ +
#best/views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def youngest(request):
+    list_teams = Team.objects.filter(team_level__exact="U09")
+    context = {'youngest_teams': list_teams}
+    return render(request, 'best/index.html', context)
+
+ +
+
+ +

データのレンダリング

+ +

ウェブフレームワークは通常テンプレートシステムを提供します。これにより、ページが生成されたときに追加されるデータのプレースホルダーを使用して、出力文書の構造を指定できます。テンプレートは HTML の作成によく使用されますが、他の種類の文書も作成できます。

+ +

ウェブフレームワークは多くの場合、{{glossary("JSON")}} や {{glossary("XML")}} など、格納されているデータから他のフォーマットを簡単に生成するためのメカニズムを提供します。

+ +

たとえば、Django テンプレートシステムでは、"double-handlebars" 構文 (例えば {{ variable_name }}) を使って変数を指定することができます。これは、ページがレンダリングされるときにビュー関数から渡された値に置き換えられます。テンプレートシステムは式のサポート (構文: {% expression %}) も提供します。これによりテンプレートは、渡されたリスト値を繰り返すような単純な操作を実行できます。

+ +
+

メモ: Jinja2 (Python)、handlebars (JavaScript)、moustache (JavaScript) など、他の多くのテンプレートシステムでも同様の構文が使用されています。

+
+ +

以下のコードスニペットはこれがどのように機能するかを示しています。前のセクションの「最年少チーム」の例を続けると、HTML テンプレートにはビューによって youngest_teams というリスト変数が渡されます。HTML スケルトンの内部には、最初に youngest_teams 変数が存在するかどうかを確認し、次に for ループ内でそれを繰り返す式があります。各イテレーションで、テンプレートはチームの team_name 値をリストアイテムに表示します。

+ +
#best/templates/best/index.html
+
+<!DOCTYPE html>
+<html lang="en">
+<body>
+
+ {% if youngest_teams %}
+    <ul>
+    {% for team in youngest_teams %}
+        <li>\{\{ team.team_name \}\}</li>
+    {% endfor %}
+    </ul>
+{% else %}
+    <p>利用できるチームがありません。</p>
+{% endif %}
+
+</body>
+</html>
+
+ +

ウェブフレームワークの選び方

+ +

使いたいと思うかもしれないほとんどすべてのプログラミング言語には多数のウェブフレームワークが存在します (次のセクションでもっと人気のあるフレームワークの一部をリストします)。選択肢が多すぎると、どのウェブフレームワークが新しいウェブアプリケーションの最適な出発点になるかを判断するのが難しくなります。

+ +

決定に影響を与える可能性がある要因の一部は以下のとおりです。

+ + + +

ライセンス、フレームワークが活発に開発されているかどうかなど、他にも多くの要因が考えられます。

+ +

プログラミングの完全な初心者であるならば、おそらく「学びやすさ」に基づいてフレームワークを選ぶでしょう。言語自体の「使いやすさ」に加えて、高品質のドキュメント/チュートリアル、および新しいユーザーを支援する活発なコミュニティが最も貴重なリソースです。コースの後半で例を書くために Django (Python) と Express (Node/JavaScript) を選択しました。これは主にそれらが習得が容易で優れたサポートがあるためです。

+ +
+

メモ: Django (Python) と Express (Node/JavaScript) のメインウェブサイトに行き、それらのドキュメントとコミュニティを調べてみましょう。

+ +
    +
  1. メインサイトに移動する (上記のリンク先) +
      +
    • Documentation メニューのリンク (Documentation、Guide、API Reference、Getting Started など) をクリックします。
    • +
    • URL ルーティング、テンプレート、データベース/モデルの設定方法を説明したトピックはありますか?
    • +
    • ドキュメントは明確ですか?
    • +
    +
  2. +
  3. 各サイトのメーリングリストに移動します (コミュニティリンクからアクセス可能)。 +
      +
    • 過去数日間に投稿された質問の数
    • +
    • 回答はいくつありますか?
    • +
    • 彼らは活発なコミュニティを持っていますか?
    • +
    +
  4. +
+
+ +

いくつかの良いウェブフレームワークとは?

+ +

それでは次に、いくつかのサーバーサイドウェブフレームワークについて説明しましょう。

+ +

以下のサーバーサイドフレームワークは、執筆時点で入手可能な最も人気のあるものの一部を表しています。オープンソースで、活発に開発されており、熱心なコミュニティがドキュメントを作成し、ディスカッション掲示板でユーザーを支援しています。また、多数の有名ウェブサイトで使用されています。他にも基本的なインターネット検索を使用して見つけられる多くの素晴らしいサーバーサイドフレームワークがあります。

+ +
+

メモ: 説明は (一部) フレームワークウェブサイトから引用しています。

+
+ +

Django (Python)

+ +

Django は、迅速な開発とクリーンで実用的なデザインを促進する高レベルの Python ウェブフレームワークです。

+ +

経験豊富な開発者によって構築されており、ウェブ開発における面倒なことの大部分を世話します、そのため、車輪の再発明をする必要なく、アプリの作成に集中できます。無料でオープンソースです。

+ +

Django は「バッテリー同梱」という哲学に従い、ほとんどの開発者が「一般的に」実行したいと思うほとんどすべてのことを提供します。すべてが含まれているので、すべて一緒に機能し、一貫した設計原理に従い、そして広範囲かつ最新のドキュメントがあります。また、高速で安全、そして非常にスケーラブルです。Python をベースにしているので、Django のコードは読みやすく、保守も簡単です。

+ +

(Django のホームページから) Django を使っている人気のあるサイトには、Disqus、Instagram、Knight Foundation、MacArthur Foundation、Mozilla、National Geographic、Open Knowledge Foundation、Pinterest、Open Stack などがあります。

+ +

Flask (Python)

+ +

Flask は Python 用のマイクロフレームワークです。

+ +

最小構成ですが、Flask は一般的に真面目なウェブサイトを作成することができます。開発サーバーとデバッガーが含まれており、Jinja2 テンプレート、セキュアクッキー、ユニットテスト、および RESTful リクエストのディスパッチをサポートしています。良いドキュメントと活発なコミュニティも持っています。

+ +

Flask は、特に小規模でリソースに制約のあるシステムでウェブサービスを提供する必要がある開発者 (たとえば、Raspberry Pi でウェブサーバーを実行する、Drone コントローラーなど) にとって非常に人気があります。

+ +

Express (Node.js/JavaScript)

+ +

Express は、Node.js 用の高速で、独創的で、柔軟で最小限のウェブフレームワークです (node は JavaScript を実行するためのブラウザーなしの環境です)。ウェブおよびモバイルアプリケーションに堅牢な機能を提供し、便利な HTTP ユーティリティメソッドとミドルウェアを提供します。

+ +

Express はクライアントサイドの JavaScript ウェブプログラマーのサーバーサイド開発への移行が容易である、およびリソース効率が良い (基盤となるノード環境は、新しいウェブリクエストごとに別々のプロセスを生成するのではなく、スレッド内で軽量のマルチタスクを使用します) という部分で、非常に人気があります。

+ +

Express は最小限のウェブフレームワークであるため、使用するすべてのコンポーネントが組み込まれているわけではありません (たとえば、データベースへのアクセス、ユーザーおよびセッションのサポートは、独立したライブラリーを通じて提供されます)。多くの優れた独立したコンポーネントがありますが、特定の目的に最適なものを見つけるのが難しい場合があります。

+ +

FeathersItemsAPIKeystoneJSKrakenLoopBackMEAN、および Sails を含む、多くの一般的なサーバーサイドおよびフルスタックフレームワーク (サーバーサイドフレームワークとクライアントサイドフレームワークの両方を含む) がExpress に基づいています。

+ +

Uber、Accenture、IBM などを含む多くの有名企業が Express を使用しています (リストはこちら)。

+ +

Ruby on Rails (Ruby)

+ +

Rails (通常 "Ruby on Rails" と呼ばれる) は、Ruby プログラミング言語用に書かれたウェブフレームワークです。

+ +

Rails は Django と非常によく似た設計思想に従っています。 Django と同様に、URL のルーティング、データベースからのデータへのアクセス、テンプレートからの HTML の生成、および {{glossary("JSON")}} または {{glossary("XML")}} としてのデータの書式設定のための標準メカニズムを提供します。それは同様に DRY ("自分で繰り返してはいけない" — 可能であれば一度だけコードを書く)、MVC (model-view-controller) そして他の多くのようなデザインパターンの使用を奨励します。

+ +

もちろん、特定の設計上の決定と言語の性質により、多くの違いがあります。

+ +

Rails は、BasecampGitHubShopifyAirbnbTwitchSoundCloudHuluZendeskSquareHighrise などの有名なサイトに使用されています。

+ +

Laravel (PHP)

+ +

Laravel は表現力豊かで洗練された構文を持つウェブアプリケーションフレームワークです。Laravel は、次のようなウェブプロジェクトの大部分で使用されている一般的な作業を軽減することで、開発の手間を省くことを試みています。

+ + + +

Laravel はアクセシビリティに優れながら強力であり、大規模で堅牢なアプリケーションに必要なツールを提供します。

+ +

ASP.NET

+ +

ASP.NET は、現代のウェブアプリケーションおよびサービスを構築するために Microsoft によって開発されたオープンソースウェブフレームワークです。ASP.NET を使用すると、HTML、CSS、および JavaScript に基づいてウェブサイトを迅速に作成し、何百万ものユーザーが使用できるように拡張し、ウェブ API、データフォーム、リアルタイム通信などのより複雑な機能を簡単に追加できます。

+ +

ASP.NET の差別化要因の1つは、共通言語ランタイム (CLR) 上に構築されていることです。プログラマは、サポートされている .NET 言語 (C#、Visual Basic など) を使用して ASP.NET コードを書くことができます。多くのマイクロソフト製品と同様に、優れたツール (多くの場合無料)、活発な開発者コミュニティ、および適切に記述されたドキュメントから恩恵を受けます。

+ +

ASP.NET は、Microsoft、Xbox.com、Stack Overflow、その他多くのユーザーによって使用されています。

+ +

Mojolicious (Perl)

+ +

Mojolicious は、Perl プログラミング言語用の次世代ウェブフレームワークです。

+ +

ウェブの初期の頃、CGI と呼ばれる素晴らしい Perl ライブラリーのおかげで、多くの人が Perl を学びました。それは言語についてあまり知らなくても始めるには十分に単純で、続けるには十分に強力でした。Mojolicious は、最先端のテクノロジを使ってこのアイデアを実現しています。

+ +

Mojolicious が提供する機能のいくつかは以下の通りです。
+ リアルタイムウェブフレームワーク、つまり単一ファイルのプロトタイプを構造化された MVC ウェブアプリケーションに簡単に拡張できます。RESTful なルート、プラグイン、コマンド、Perl 風のテンプレート、コンテンツネゴシエーション、セッション管理、フォーム検証、テストフレームワーク、静的ファイルサーバー、CGI/PSGI 検出、ファーストクラス Unicode サポート。IPv6、TLS、SNI、IDNA、HTTP/SOCKS 5 プロキシー、UNIX ドメインソケット、Comet (ロングポーリング)、キープアライブ、コネクションプーリング、タイムアウト、Cookie、マルチパートおよび gzip 圧縮サポートを備えたフルスタックの HTTP および WebSocket クライアント/サーバー実装。CSS セレクターをサポートする JSON および HTML/XML パーサーおよびジェネレーター。隠された魔法のない、とてもきれいで、移植性があり、オブジェクト指向の純粋な Perl API。
+ 長年の経験に基づく新鮮なコード、無料およびオープンソース。

+ +

Spring Boot (Java)

+ +

Spring Boot は、Spring が提供している数多くのプロジェクトのうちの 1 つです。Java を使用してサーバーサイドのウェブ開発を行う良い出発点です。

+ +

Java をベースとした唯一のフレームワークではないことは間違いありませんが、スタンドアローンのプロダクショングレードの Spring ベースのアプリケーションを簡単に実行することができます。これは Spring プラットフォームと他社製ライブラリーの見解に基づいた見方ですが、最小限の手間と設定で始めることができます。

+ +

小さな問題にも使用できますが、その強みはクラウドアプローチを使用する大規模アプリケーションを構築することです。通常、複数のアプリケーションが互いに並行して実行され、ユーザーとのインタラクションを提供するものと、バックエンド作業を実行するもの (データベースやその他のサービスへのアクセスなど) のみがあります。ロードバランサーは、冗長性と信頼性を確保したり、ユーザーのリクエストを地理的に処理してレスポンス性を確保したりするのに役立ちます。

+ +

まとめ

+ +

この記事では、ウェブフレームワークによって、サーバーサイドコードの開発と保守が容易になることを示しました。また、いくつかの一般的なフレームワークの概要とウェブアプリケーションフレームワークの選択基準についても説明しました。これで、少なくとも自身のサーバーサイド開発用のウェブフレームワークを選択する方法についてのアイデアが得られました。 そうでなくても心配しないでください。コースの後半で Django と Express に関する詳細なチュートリアルを提供して、ウェブフレームワークを実際に使用する経験をいくつか提供します。

+ +

このモジュールの次の記事では、方向を少し変えてウェブセキュリティについて考えます。

+ +

{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}

+ +

このモジュールの記事一覧

+ + diff --git a/files/ja/learn/server-side/first_steps/website_security/index.html b/files/ja/learn/server-side/first_steps/website_security/index.html new file mode 100644 index 0000000000..59c9a7dbf7 --- /dev/null +++ b/files/ja/learn/server-side/first_steps/website_security/index.html @@ -0,0 +1,181 @@ +--- +title: Web サイトのセキュリティ +slug: Learn/Server-side/First_steps/Website_security +tags: + - CodingScripting + - Web サイトセキュリティ + - Web セキュリティ + - イントロダクション + - ガイド + - サーバサイドプログラミング + - セキュリテイ + - 初心者 + - 学習 +translation_of: Learn/Server-side/First_steps/Website_security +--- +
{{LearnSidebar}}
+ +
{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}
+ +

ウェブサイトのセキュリティでは、ウェブサイトのデザインと使用方法のあらゆる面で警戒が必要です。この入門記事だけではウェブサイトのセキュリティの第一人者にはなれませんが、脅威がどこから発生するのか、そして最も一般的な攻撃に対してウェブアプリケーションを強化するために何ができるのかを理解するのに役立ちます。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシー
目標:ウェブアプリケーションのセキュリティに対する最も一般的な脅威と、サイトがハッキングされるリスクを減らすためにできることを理解する。
+ +

ウェブサイトのセキュリティとは?

+ +

インターネットは危険な場所です。定期的に、サービス拒否攻撃によってウェブサイトが利用できなくなったり、自分のホームページに変更された (多くの場合有害な) 情報を表示したりすることがあります。その他の注目を集める事例では、何百万ものパスワード、メールアドレス、およびクレジットカードの詳細がパブリックドメインに漏洩し、ウェブサイトの利用者を個人的な当惑と経済的リスクの両方にさらしています。

+ +

ウェブサイトのセキュリティの目的は、これらの (または任意の) 種類の攻撃を防ぐことです。ウェブサイトセキュリティのより正式な定義は、許可されていないアクセス、使用、改変、破壊、または混乱からウェブサイトを保護することです

+ +

効果的なウェブサイトセキュリティでは、ウェブアプリケーション、Web サーバーの設定、パスワードの作成と更新に関するポリシー、およびクライアント側のコードなど、ウェブサイト全体にわたる設計作業が必要です。すべて不吉に聞こえるかもしれませんが、サーバーサイド Web フレームワークを使用している場合、多くの一般的な攻撃に対して「デフォルトで」堅牢でよく考え抜かれた防御メカニズムがほぼ確実に有効になります。HTTPS を有効にするなど、他の攻撃は Web サーバーの設定を通じて軽減できます。最後に、明らかな間違いを犯したかどうかを確認するのに役立つ、公開されている脆弱性スキャナツールがあります。

+ +

この記事の残りの部分では、いくつかの一般的な脅威と、サイトを保護するために実行できる簡単な手順の詳細について説明します。

+ +
+

メモ: これは導入トピックであり、ウェブサイトのセキュリティについて考え始めるのに役立つように設計されていますが、網羅的なものではありません。

+
+ +

ウェブサイトのセキュリティ上の脅威

+ +

このセクションでは、最も一般的なウェブサイトの脅威をいくつか紹介し、それらがどのように軽減されるのかを示します。お読みになったところでは、ウェブアプリケーションがブラウザーから来るデータについて信頼しているか、または十分に妄想的ではない場合に、脅威が最も効果的であることに注意してください。

+ +

クロスサイトスクリプティング (XSS)

+ +

XSS は、攻撃者がウェブサイトを通じて他のユーザーのブラウザーにクライアントサイドのスクリプトを挿入することを可能にする一連の攻撃を表すために使用される用語です。注入されたコードはサイトからブラウザーに送信されるため、コードは信頼されており、ユーザーのサイト認証 Cookie を攻撃者に送信するなどのことが可能です。攻撃者が Cookie を持っていると、あたかもユーザーであるかのようにサイトにログインし、クレジットカードの詳細へのアクセス、連絡先の詳細の表示、パスワードの変更など、ユーザーができることなら何でもできます。

+ +
+

メモ: XSS 脆弱性は、他のどの種類のセキュリティの脅威よりも歴史的に一般的です。

+
+ +

XSS 脆弱性は、サイトが挿入されたスクリプトをブラウザーに返す方法に基づいて、反映型永続型に分けられます。

+ + + +

POST または GET リクエストからのデータが XSS の脆弱性の最も一般的な原因ですが、ブラウザーによって表示される Cookie データやアップロードされて表示されるユーザーファイルなど、ブラウザーからのデータはすべて潜在的に脆弱です。

+ +

XSS の脆弱性に対する最善の防御策は、コードを実行するための命令を含む可能性があるマークアップを削除または無効にすることです。HTML の場合、これには <script><object><embed>、および <link> などの要素が含まれます。

+ +
+

スクリプトを実行したり、サーバーコードの実行に影響を与えたりすることができないようにユーザーデータを変更するプロセスは、入力サニタイズと呼ばれます。多くのウェブフレームワークは、デフォルトで HTML フォームからのユーザー入力を自動的にサニタイズします。

+
+ +

SQL インジェクション

+ +

SQL インジェクションの脆弱性により、悪意のあるユーザーはデータベース上で任意の SQL コードを実行することができ、ユーザーの許可に関係なくデータへのアクセス、変更、削除ができます。インジェクション攻撃が成功すると、ID を偽装したり、管理者権限を持つ新しい ID を作成したり、サーバー上のすべてのデータにアクセスしたり、データを破壊または変更して使用できなくなる可能性があります。

+ +
+

SQL インジェクションの種類には、エラーベースの SQL インジェクション、ブールエラーに基づく SQL インジェクション、および時間ベースの SQL インジェクションがあります。

+
+ +

ベースとなる SQL ステートメントに渡されるユーザー入力がステートメントの意味を変更する可能性がある場合に、この脆弱性が存在します。たとえば、次のコードは、HTML フォームから提供された特定の名前 (userName) を持つすべてのユーザーを一覧表示することを目的としています。

+ +
statement = "SELECT * FROM users WHERE name = '" + userName + "';"
+ +

ユーザーが実名を指定した場合、そのステートメントは意図したとおりに機能します。ただし、悪意のあるユーザーは userName に太字のテキストを指定するだけで、この SQL ステートメントの動作を次の例の新しいステートメントに完全に変更する可能性があります。

+ +
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
+
+ +

変更された文は、users テーブルを削除し、userinfo テーブルからすべてのデータを選択する (すべてのユーザーの情報を表示する) 有効な SQL 文を作成します。これは、挿入されたテキストの最初の部分 (a';) が元の文を完成させるために機能します。

+ +

この種の攻撃を回避するには、SQL クエリに渡されるユーザーデータがクエリの性質を変更できないようにする必要があります。これを行う 1 つの方法は、SQL で特別な意味を持つユーザー入力内のすべての文字をエスケープすることです。

+ +
+

メモ: SQL ステートメントは、' 文字を文字列リテラルの開始と終了として扱います。この文字の前に円記号を入れる (\') ことで、シンボルをエスケープし、代わりにそれを文字 (文字列の一部) として扱うように SQL に指示します。

+
+ +

次の文では、' 文字をエスケープします。SQL は名前を太字の文字列全体として解釈します (これは非常に奇妙な名前ですが、有害ではありません)。

+ +
SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';
+
+ +

ウェブフレームワークはしばしばあなたのためにエスケープする文字の面倒を見るでしょう。たとえば、Django はクエリセット (モデルクエリ) に渡されたユーザーデータが確実にエスケープされるようにします。

+ +
+

メモ: このセクションはここウィキペディアの情報に大きく依存しています。

+
+ +

クロスサイトリクエストフォージェリ (CSRF)

+ +

CSRF 攻撃は、悪意のあるユーザーが他のユーザーの資格情報を使用して、そのユーザーの知らないうちに同意なしでアクションを実行することを可能にします。

+ +

この種の攻撃は、例で最もよく説明されています。John は、特定のサイトでログインユーザーがアカウント名と金額を含む HTTP POST リクエストを使用して特定のアカウントに送金できることを知っている悪意のあるユーザーです。John は、自分の銀行の詳細と金額を隠しフィールドとして含むフォームを作成し、それを他のサイトユーザーにメールで送信します ([送信] ボタンは [早く金持ちになる] サイトへのリンクとして偽装)。

+ +

ユーザーが[送信]ボタンをクリックすると、トランザクションの詳細と、サイトに関連付けられているブラウザーが要求したクライアント側の Cookie を含む HTTP POST リクエストがサーバーに送信されます (リクエストに関連サイトの Cookie を追加するのは通常のブラウザーの動作です)。サーバーは Cookie をチェックし、それらを使用してユーザーがログインしていてトランザクションを実行する権限を持っているかどうかを判断します。

+ +

その結果、取引サイトにログインしている間に [送信] ボタンをクリックしたすべてのユーザーが取引を行うことになります。John は金持ちになります。

+ +
+

メモ: ここでのトリックは、John がユーザーの cookie (またはアクセス資格情報) にアクセスする必要がないことです。ユーザーのブラウザーはこの情報を保存し、関連するサーバーへのすべてのリクエストに自動的に含めます。

+
+ +

この種の攻撃を防ぐ 1 つの方法は、サーバーが POST リクエストにユーザー固有のサイト生成のシークレット情報を含めることを要求することです。転送に使用されるウェブフォームを送信するときに、シークレットがサーバーによって提供されます。この方法では、サーバーからユーザーに提供されているシークレットを知っている必要があるため、John は独自のフォームを作成できません。たとえ彼がシークレットを見つけて特定のユーザーのためにフォームを作成したとしても、彼はもはやその同じフォームを使用してすべてのユーザーを攻撃することはできないでしょう。

+ +

ウェブフレームワークには、そのような CSRF 防止メカニズムが含まれていることがよくあります。

+ +

その他の脅威

+ +

その他の一般的な攻撃/脆弱性は次のとおりです。

+ + + +

ウェブサイトのセキュリティ脅威の包括的な一覧については、Category: Web security exploits (Wikipedia) および Category: Attack (Open Web Application Security Project) を参照してください。

+ +

いくつかの重要なメッセージ

+ +

ウェブアプリケーションがブラウザーからのデータを信頼している場合、前のセクションのセキュリティ上の悪用のほとんどすべてが成功します。ウェブサイトのセキュリティを向上させるために他に何をしても、ブラウザーから表示される前、SQL クエリで使用される前、または OS やファイルシステムの呼び出しに渡される前に、すべてのユーザー発信データをサニタイズする必要があります。

+ +
+

重要:ウェブサイトのセキュリティについて学ぶことができる最も重要な教訓は、ブラウザーからのデータを決して信用しないことです。これには GET リクエスト、POST リクエスト、HTTP ヘッダと Cookie、およびユーザーがアップロードしたファイルの URL パラメータのデータが含まれますが、これらに限りません。すべての受信データを常にチェックしてサニタイズしてください。常に最悪の事態を想定してください。

+
+ +

あなたが取れる他の具体的な対策はいくつかあります:

+ + + +

ウェブフレームワークは、より一般的な脆弱性の多くを軽減するのに役立ちます。

+ +

まとめ

+ +

この記事では、ウェブセキュリティの概念と、ウェブサイトが保護しようとする一般的な脅威について説明しました。最も重要なことは、ウェブアプリケーションはウェブブラウザーからのデータを信頼できないということです。すべてのユーザーデータは、表示する前にサニタイズするか、SQL クエリやファイルシステムコールで使用する必要があります。

+ +

この記事で、モジュールの終わりに来ました。サーバーサイドのウェブサイトプログラミングの最初のステップをカバーしました。これらの基本概念を学んで楽しんでいただければ幸いです。これでウェブフレームワークを選択してプログラミングを開始する準備が整いました。

+ +

{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}

+ +

このモジュールの記事一覧

+ + diff --git a/files/ja/learn/server-side/index.html b/files/ja/learn/server-side/index.html new file mode 100644 index 0000000000..728f2c0fac --- /dev/null +++ b/files/ja/learn/server-side/index.html @@ -0,0 +1,59 @@ +--- +title: サーバサイド Web サイトプログラミング +slug: Learn/Server-side +tags: + - CodingScripting + - Landing + - NeedsTranslation + - Server + - Server-side programming + - Topic + - TopicStub + - 初学者 + - 学習 + - 導入部 +translation_of: Learn/Server-side +--- +
{{LearnSidebar}}
+ +

Dynamic(動的な)Webサイト サーバサイドプログラミング のトピックは、動的な Web サイト (HTTP リクエストに応じてカスタマイズされた情報を届ける Web サイト) の作り方を説明する一連のモジュールです。各モジュールは、一般的なサーバサイドプログラミングへの導入となる情報を提供するほか、特に初学者に向けて、Django (Python) や Express (Node.js/JavaScript) といった基礎的なアプリケーションを構築するためのWebフレームワークの使用方法について説明しています。

+ +

ほとんどの有名な Web サイトでは、必要に応じてさまざまなデータを動的に表示するために、何らかのサーバサイドの技術を使用しています。例えば Amazon の場合を考えてみましょう。非常にたくさんの商品が扱われていますね。また Facebook にも大量の投稿があります。これら全てを表示するために別々の静的なページをいくつも作っていっても、絶対に非効率です。ですのでその代わりに、こういったサイトでは静的なテンプレート (HTML, CSS, JavaScript で構築します) を表示し、テンプレートの内部で必要に応じ、表示される情報を動的に更新しています。例えば、Amazon で今見ている商品とは違う商品を見ようとするときに、この更新がおこなわれています。

+ +

現代の Web 開発においては、サーバサイド開発を学ぶことが強く推奨されています。

+ +

学習の道筋

+ +

サーバサイドプログラミングを始めるのは、一般的にいってクライアントサイドの開発を始めるよりも簡単です。なぜなら動的な Web サイトはどれも非常によく似た操作 (データベースからデータを読み出してそれをページに表示し、ユーザが入力したデータをヴァリデーションしてそれをデータベースに保存し、ユーザの権限をチェックした後にユーザをログインさせる、といったようなことです) を行うからです。また、動的な Web サイトはこういった一般的な Web サーバ側での操作を簡単にしてくれる Web・フレームワークを用いて作られているからです。

+ +

プログラミング概念の基本知識 (あるいは特定のプログラミング言語の知識) は役立ちますが、必要ではありません。つまり、クライアントサイドコーディングの熟達は必要ではなく、基本知識はクライアントサイドの Web「フロントエンド」の開発者とうまく共同作業するのに役立つでしょう。

+ +

「Webの動作原理」を理解する必要があります。最初に次のトピックを読むのをお勧めします:

+ + + +

基本的な理解があれば、この節のモジュールに進む準備ができているでしょう。

+ +

モジュール

+ +

このトピックは次のモジュールがあります。最初のモジュールから始めるべきで、次に続くモジュールのどれかに進んで、そこでWebフレームワークを使ってとても有名な2つのサーバサイド言語を使う方法が示されます。

+ +
+
サーバサイド Web サイトプログラミングの最初の一歩
+
このモジュールではサーバ技術の無知な人向けに、サーバサイド Web プログラミングについての情報を提供します。それにはサーバサイドプログラミングについての基本的な質問 — "それは何なのか", "クライアントサイドプログラミングと何が違うのか", "なぜ便利なのか" — や、サーバサイド Web フレームワークの概要や、あなたのサイトに最も合うものを選ぶ方法のガイドが含まれます。最後に Web サーバセキュリティの入門セクションがあります。
+
Django Web フレームワーク (Python)
+
Django は Python で書かれた、極めて人気のある、完全な機能のあるサーバサイドフレームワークです。このモジュールではなぜ Django がこうも良い Web サーバフレームワークであるのかや、開発環境のセットアップ方法や、これを使ってよくあるタスクを行う方法を説明します。
+
Express Web フレームワーク (Node.js/JavaScript)
+
Express は JavaScript で書かれて node.js の実行環境でホストされる、人気のある Web フレームワークです。このモジュールではこのフレームワークの主な利点や、開発環境のセットアップ方法や、よくある Web 開発・デプロイのタスクを行う方法を説明します。
+
+ +

あわせて参照

+ +
+
フレームワークなしの Node サーバ
+
この記事では、フレームワークを使用せずに、Node.jsだけで構築された単純な静的ファイルサーバを紹介します。
+
diff --git a/files/ja/learn/server-side/node_server_without_framework/index.html b/files/ja/learn/server-side/node_server_without_framework/index.html new file mode 100644 index 0000000000..a7ca7493b5 --- /dev/null +++ b/files/ja/learn/server-side/node_server_without_framework/index.html @@ -0,0 +1,163 @@ +--- +title: フレームワークなしの Node.js サーバ +slug: Learn/Server-side/Node_server_without_framework +tags: + - JavaScript + - NeedsContent + - Node + - サーバ + - フレームワークなし +translation_of: Learn/Server-side/Node_server_without_framework +--- +
{{LearnSidebar}}
+ +

この記事では、フレームワークを使用せずに、Node.jsだけで構築された単純な静的ファイルサーバを紹介します。

+ +

Node.js用に、サーバを稼働させるのに役立つ多くのフレームワークがあります。

+ +

最も人気があるのは、次のようなものです:

+ + + +

これらは、どんな状況にも適しているというわけではありません。開発者は既存のフレームワークに依存することなく、独自のサーバを構築する必要があることもあるでしょう。

+ +

静的ファイルサーバーの例

+ +

Node.jsで構築された、簡単な静的ファイルサーバの例を以下に示します。

+ +
var http = require('http');
+var fs = require('fs');
+var path = require('path');
+
+http.createServer(function (request, response) {
+    console.log('request ', request.url);
+
+    var filePath = '.' + request.url;
+    if (filePath == './') {
+        filePath = './index.html';
+    }
+
+    var extname = String(path.extname(filePath)).toLowerCase();
+    var mimeTypes = {
+        '.html': 'text/html',
+        '.js': 'text/javascript',
+        '.css': 'text/css',
+        '.json': 'application/json',
+        '.png': 'image/png',
+        '.jpg': 'image/jpg',
+        '.gif': 'image/gif',
+        '.wav': 'audio/wav',
+        '.mp4': 'video/mp4',
+        '.woff': 'application/font-woff',
+        '.ttf': 'application/font-ttf',
+        '.eot': 'application/vnd.ms-fontobject',
+        '.otf': 'application/font-otf',
+        '.svg': 'application/image/svg+xml'
+    };
+
+    var contentType = mimeTypes[extname] || 'application/octet-stream';
+
+    fs.readFile(filePath, function(error, content) {
+        if (error) {
+            if(error.code == 'ENOENT') {
+                fs.readFile('./404.html', function(error, content) {
+                    response.writeHead(200, { 'Content-Type': contentType });
+                    response.end(content, 'utf-8');
+                });
+            }
+            else {
+                response.writeHead(500);
+                response.end('Sorry, check with the site admin for error: '+error.code+' ..\n');
+                response.end();
+            }
+        }
+        else {
+            response.writeHead(200, { 'Content-Type': contentType });
+            response.end(content, 'utf-8');
+        }
+    });
+
+}).listen(8125);
+console.log('Server running at http://127.0.0.1:8125/');
+ +

各部の説明

+ +

第1行から第3行までは、Node.jsが提供するモジュールを組み込みます。おおむね「インポート」に似たような手続きです。

+ +
var http = require('http');
+var fs = require('fs');
+var path = require('path');
+
+ +

次にある関数で、サーバーを生成します。 https.createServerは、サーバーオブジェクトを返しますが、下の例ではポート8125で要求の受付を開始します。

+ +
http.createServer(function (request, response) {
+    ...
+}).listen(8125);
+
+ +

次の4行では、要求があったURLから、ファイルへのパスを決定します。ファイル名が明示されていないときは、デフォルト名を使うようにします。

+ +
var filePath = '.' + request.url;
+if (filePath == './') {
+    filePath = './index.html';
+}
+
+ +

例えば、example.orgというURLを要求されたときは、example.org/index.html.のことだと解釈します。

+ +

次に、要求されたファイルの拡張子を調べ、以下に定義するMIMEタイプのどれかと一致したら、そのタイプを使います。一致しない場合には、デフォルトのタイプapplication/octet-streamを使うようにします。.

+ +
var mimeTypes = {
+    '.html': 'text/html',
+    '.js': 'text/javascript',
+    '.css': 'text/css',
+    '.json': 'application/json',
+    '.png': 'image/png',
+    '.jpg': 'image/jpg',
+    '.gif': 'image/gif',
+    '.wav': 'audio/wav',
+    '.mp4': 'video/mp4',
+    '.woff': 'application/font-woff',
+    '.ttf': 'application/font-ttf',
+    '.eot': 'application/vnd.ms-fontobject',
+    '.otf': 'application/font-otf',
+    '.svg': 'application/image/svg+xml'
+};
+
+var contentType = mimeTypes[extname] || 'application/octet-stream';
+
+ +

最後に、ファイルの情報をクライアントに返送します。この関数では、あらかじめ用意してあったfilePath変数を使ってファイルを読み込みます。

+ +
fs.readFile(filePath, function(error, content) {
+    ...
+});
+
+ +

関数のなかで最初にやることは、起こりうるエラーへの対応です。一番多いのは、存在しないファイルを要求された場合(ENOENT)で、エラーコード404に対応するページを返してやります。

+ +
if(error.code == 'ENOENT') {
+    fs.readFile('./404.html', function(error, content) {
+        response.writeHead(200, { 'Content-Type': contentType });
+        response.end(content, 'utf-8');
+    });
+}
+else {
+    response.writeHead(500);
+    response.end('Sorry, check with the site admin for error: '+error.code+' ..\n');
+    response.end();
+}
+ +

何もエラーが検出されなかったら、MIME型をヘッダーに付けて、要求されたファイルを返してやります。

+ +
response.writeHead(200, { 'Content-Type': contentType });
+response.end(content, 'utf-8');
+ +

拡張の検討

+ +

静的なファイルの返送機能だけでなく、要求の度にページを動的に生成する機能を付け加えることを考えてみてください。

-- cgit v1.2.3-54-g00ecf