Spring Bootによるウェブアプリ開発: [補足01] ユーザ認証(2)

前回の続き。

アカウントの情報がハードコードだと管理が面倒なので、データベースを使おう。その際、保存するパスワードはハッシュ化しよう。

作成・変更したファイル

今回使うページを次に示す。

パス アクセス制限 備考
/suppl01/admin/ 管理人ロールをもつ登録ユーザ 管理人用トップページ
/suppl01/admin/add_user 管理人ロールをもつ登録ユーザ ユーザ登録フォーム

アカウント情報を格納するテーブル

40.1 User Schemaにあるようにテーブルを作ると、簡単にアカウント情報として利用することができる(もちろん設定をすれば他の構成でも使えるが)。今回は、これを参考に次のSQL命令でテーブルを作ることにする。

40.1 User Schemaからの変更点は以下の通り。

  • passwordカラムは、ハッシュ化したパスワードを保存する為にサイズを100に変更
  • すでに存在している場合は作成しないようにするために「IF NOT EXISTS」を追加

このSQL命令をH2コンソールで実行してもいいし、JdbcTemplateで実行してもいいのだが、ファイルに記述したSQL命令をアプリの起動時に実行する方法(78.3 Initialize a database using Spring JDBC)がある。
今回はこの方法を使うことにし、src/main/resources/schema.sqlを作成する。

WebSecurityConfigurerAdapterを継承した設定用クラス

参考: 5.6.2 JDBC Authentication

  • 16行目: 設定時にJdbcTemplate(かDataSourcedの)インスタンスが必要なので、引数として「JdbcTemplate jdbcTemplate』を追加し、DIコンテナから取得する。
  • 21行目: パスワードのエンコーダとして使うBCryptPasswordEncoderをインスタンス化する。
  • 22行目: JDBCを使った認証で、どのデータベースを使うか(jdbcTemplate.getDataSource())とどのパスワードエンコーダを使うか(passwordEncoder)を指定する。
  • 29行目: 「/suppl01/admin/**」へのアクセスへは管理者ロールを持つアカウントがアクセスできることを指定する。

コントローラ

  • 86行目: SecurityConfiguration.javaと同様にパスワードエンコーダをインスタンス化する。
  • 87ページ: パスワードエンコーダのencodeメソッドを使ってパスワードをエンコードしたものを保存する。
  • 88ページ: ロールをauthoritiesテーブルに保存する。usersテーブルの情報だけではダメなことに注意する。

テンプレート

add_user.html

  • 14行目: ロールは「ROLE_」をつけることに注意する。

動作確認

ハードコードしたadminでログインし、一般ユーザ、管理者ユーザを作成し、それらのユーザで正しくアクセスできること等を確かめてみよう。

Spring Bootによるウェブアプリ開発: [補足01] ユーザ認証(1)

フォームからユーザ名とパスワードを入力してログインする機能(ユーザ認証)を実現しよう!

作成・変更したファイル

今回使うページを次に示す。

パス アクセス制限 備考
/suppl01/ 誰でも トップページ
/suppl01/login 誰でも ログインフォーム
/suppl01/loginProcess 誰でも ログイン処理を行う(ログインフォームからのPOST先)
/suppl01/logout 誰でも ログアウト処理を行う(ログアウトフォームからのPOST先)
/suppl01/secret/ 登録ユーザ ログイン後に遷移するページ
/suppl01/secret/info 登録ユーザ アカウントの情報を表示するページ

Spring Securityの有効化とBasic認証の無効化

Spring Securityの機能を加えよう。
build.gradleのdependenciesに次の1行を加える。

これでSpring Securityの機能が有効になる。デフォルトでBasic認証の機能が有効になるので、
パスワードとユーザ名を入れないとページが見えなくなる。

Basic認証

今回はBasic認証は使わないので無効にしよう。無効にするにはapplication.propertiesに次の1行を追加する。

WebSecurityConfigurerAdapterを継承した設定用クラス

WebSecurityConfigurerAdapterクラスを継承したクラスを作ることで、ユーザ認証の設定を行うことができる。

  • 9行目: このクラスが設定用のクラスであることを示す。これに加え「@EnableWebSecurity」を記述するという解説が様々なところでなされているが、この設定については82.1 Switch off the Spring Boot security configurationを参照すること。
  • 14〜16行目: ログインできるアカウントを設定する。今回はハードコーディングする(次回、データベースに変更する)。
  • 21〜22行目: ユーザ認証を要求するページとしないページを設定する。
  • 24〜26行目: ログインに関する設定をする。ログインフォームのURLと、ログインフォームの中でどのパラメータ名(input要素のname属性)を使うか、ログインを処理するURL(ログインフォームのactionで指定するパス)、成功した時に遷移するURL、ログイン失敗した時に遷移するURLを指定する。
  • 28〜29行目: ログアウトに関する設定を行う。ログアウトを処理するURL(ログアウトフォームのactionで指定するパス)、ログアウトが成功した時に遷移するURLを指定する。

コントローラ

  • ログインを処理する「/suppl01/loginProcess」とログアウトを処理する「/suppl01/logout」はコントローラで定義しない(Spring Secutiryの機能でアクセスできるようになるため)。
  • 42行目: ユーザ情報を取得するには、@AuthenticationPrincipalアノテーションをつけたUserDetails型を引数にする。

テンプレート(解説が必要なもの)

login.html

  • 10〜12行目: ログインに失敗した場合は「/suppl01/login?error」に遷移する(SecurityConfiguration.javaを確認せよ)。つまり「error」というパラメータが付加される。「param.containsKey(‘error’)」でerrorというパラメータがあるかチェックできる(参考: request/session 属性などに対するウェブコンテキストネームスペース
  • 13行目: methodはpostにする。
  • 13行目: actionはh:action属性で指定する(単なるaction属性じゃダメ)。action属性は、th:actionでリンクURL式を使う。このようにすることによって、自動的にCSRF対策用のパラメータが付加される。
  • 13行目: URLはSecurityConfiguration.javaのloginProcessingUrlで指定したURLにする。
  • 14〜15行目: name属性はSecurityConfiguration.javaで指定した文字列を設定する。

secret/{index,info}.htmlのログアウトフォーム

  • ログインの場合と同様に、methodはpostとし、actionはth:action属性で指定する。URLはSecurityConfiguration.javaのlogoutUrlで指定したURLにする。
  • 単なるリンクにしたい場合は、logoutRequestMatcherメソッドを使った形にする(調べてみよう)。

問題点

基本的なユーザー認証の仕組みを見てきたが、このままでは不便(ユーザ情報がハードコードで管理が面倒)だし問題がある(パスワードが平文で管理されている)。次回では、ユーザ情報をデータベースに保存し、パスワードをハッシュ化して保存する方法を紹介する。

Spring Bootによるウェブアプリ開発: 起動方法(サービスの公開)

これまでアプリの開発における起動方法として、NetBeansのメニューから実行という方法を使ってきた。
この方法は開発時にはいいが、IDEのないPCにコピーしての実行や、サーバでの実行には向かない。

gradlewを使ってbootRunタスクを実行する。

これまでNetBeansからのGradleのタスクを実行してきたが、IDEを使わずにコマンドラインからも実行できる(というかこれが本来の使い方)。

ターミナル(コマンドプロンプト)で、プロジェクトのディレクトリをカレントディレクトリとし、

./gradlew bootRun

と実行する。gradlewに実行権限がなければ事前に「chmod +x gradlew」としておく。Windowsの場合は、

gradlew.bat bootRun

とする(未確認)。

FAT JARを作る。

クラスファイルやリソース等をJAR(Java Archive)という形式にまとめることができる。もともと、JARファイルは自分の作ったプログラムをまとめるもので、ライブラリ等は別に準備して実行する、という使い方が普通であった。最近は、依存するライブラリ(サーブレットコンテナ)等を全て含めて1つのJarファイルで実行ができるようにした「FAT JAR」を利用することも増えている。Spring BootではこのFAT JARも簡単に作ることができる。

./gradlew build

作られたJARファイルは、build/libに置かれる(例えば、webapp-0.0.1-SNAPSHOT.jar)。

これを実行するには、そのファイルを適当なディレクトリにコピーし、そのディレクトリをカレントディレクトリとして、次のように実行する。

java -jar webapp-0.0.1-SNAPSHOT.jar

Windowsならばダブルクリックで起動できる場合もある。しかし、標準出力に出力されるメッセージが読めないので、上記コマンドで実行するのがおすすめである。

FAT JARと同じところにstaticディレクトリをおけば、静的ファイルも扱うことができる。

作ったアプリは公開しよう

残念ながら、Javaで作ったアプリを公開できるサービスは少ない。レンタルサーバでは使えるのはPHPのみというサービスが多い。現実的には、VPSを借りJavaをインストールして実行する、ということになるだろう。

VPSを借りるという以外では、Javaが実行できるPaaSを使う方法がある(これも対応サービスは少ないが)。例えば、HEROKUならばDeploying Spring Boot Applications to Herokuというドキュメントも用意されている。条件によっては無料で使うこともできる。

Spring Bootによるウェブアプリ開発: オンラインフォトアルバム(アプリ開発例)

ポイント:

  • ファイルをアップロードする
  • フォームで日付を扱う

ソースコード

静的コンテンツ

詳しくは27.1.5 Static Contentを参照せよ。

ファイルをそのままサーバから送信するにはsrcと同じ階層に「static」というフォルダを作り、そこのファイルをおく。

static

例えば、
static/lecture12/ThumbsUp.jpgというファイルは、
http://localhost:18080/lecture12/ThumbsUp.jpgでアクセスすることができる。

また、src/main/resources/staticというフォルダも用意されており、こちらに置いたファイルにもアクセスすることができる。ただし、このフォルダの場合は、アプリ起動後に追加したものは表示できない(起動時にファイルを含めたJARファイルを作っているから)。

コントローラからこのフォルダを操作する場合そのpathを知る必要があるが、その方法の1つとしてResourceHttpRequestHandlerから得る方法がある(コントローラの53〜63行目)。ただし、この方法が標準的な方法かどうかはわからない。

画像のアップロード

  • テンプレート(add.html)
    • 11行目: ファイルを送る場合はform要素に「enctype=”multipart/form-data”」という属性をつける。
    • 16行目: 「type=”file”」とするとファイルアップロード用のフォームができる。
  • コントローラ(Lecture12Controller.java)
    • 99行目: ファイルはMultipartFile型で受け取ることができる。
    • 101〜108行目: 受け取ったファイルは、サーバ上のテンポラリ領域に置かれるファイルなので、transferToメソッドを使って実際に使う場所にファイルを移す。移す先をFileクラスのインスタンスで指定する。このtransferToメソッドは検査例外(教科書P.66)が発生する可能性があるので、例外処理を行う。

なお、フォームで送ることのできるデータのサイズには制限がある。これを変更するには以下の2行をapplications.propertiesに加える。これは、リクエスト全体のサイズ(デフォルトは10MB)を100MBまで、リクエストに含まれる1ファイルのサイズ(デフォルトは1MB)を10MBまでに変更する設定である。

今回は画像のアップロード時にサムネイルを生成することする。
画像のスケーリングには、imgscalrを使うが、特にこれがおすすめというわけではない。また、ファイル名から拡張子を取得するのは単純な文字列処理ではあるが、今回はApache Commons IOを使うことにする。これらを使うためにbuild.gradleに次を追加する。

サムネイル生成のプログラムについては今回の主題ではないので解説は省略する。

画像の表示

  • テンプレート(index.htmlとview.html)
    • img要素(タグ)を使う
    • th:srcでsrc属性を動的に生成する
    • th:hrefと同様に@構文を使う
    • alt属性はつけよう。

日付の扱い

  • テンプレート(add.html、index.html、view.html)
    • フォームによる入力は「type=”date”」とすると対応ブラウザなら日付ピッカー等の適切なUIで入力することができる。
    • 出力する時にフォーマットするには、「th:text=”${#dates.format(photo.date, ‘yyyy年MM月dd日’)}”」等とする(Using Thymeleaf(Expression Utility Objects 日付))
  • コントローラ(Lecture12Controller.java)
    • 100行目: Date型で受け取ることができる。「@DateTimeFormat(pattern = “yyyy-MM-dd”)」でブラウザが送ってくる文字列のパターン(フォーマット)を指定する。

Spring Bootによるウェブアプリ開発: オンラインメモ(アプリ開発例)

データベースのCRUD

前回は、掲示板のデータをデータベースに格納するように(永続化するように)改良した。そこでのポイントは以下の通り。

  • Javaでデータベースを扱う(接続する)方法の1つにJDBCがあり、Spring BootにおいてはSpring JDBCという仕組みが使える(他にも色々あるがまずはこれ)。
  • H2は組み込みで使えるデータベースで、Spring Bootから簡単に使える。ウェブインターフェースも使える。
  • データベースを操作するSQL
    • CREATE TABLE: テーブルを作成する
    • SELECT: テーブルからデータを取得する
    • INSERT: テーブルにデータを投入する。
  • 具体的には、JdbcTemplateのメソッドを使って、SQLを実行する。

データベースを使った永続化の扱いの基本はCRUDと言われている。
今回はオンラインメモ帳の開発を通してCRUDを一通り実装してみよう。また、Spring Bootにおけるウェブアプリ開発で使われる基本を紹介する。

ソースコード

ポイント

テーブルの作成

前回、テーブルの作成はH2のウェブインターフェースから行ったが、これもプログラムから実行できる。下記はテーブルを作成するプログラムであるが、「IF NOT EXISTS」をつけることによって、テーブルが存在している場合は実行されない(テーブルがない時だけ実行される)。これをコントローラのコンストラクタで実行している。(Lecture11Controller.javaの31〜39行目)

レコードの編集(更新)

レコードを更新するにはUPDATEを使う。更新するカラムを「カラム名 = 値」で指定する。WHEREで更新するレコードを限定する。(Lecture11Controller.javaの101行目)

フォームのデフォルト値

フォームのデフォルト値はほとんどの場合、value属性で設定することができる。これをTymeleafで設定するには「th:value=”${変数}”』をタグにつければ良い。ただし、textareaタグはタグによって挟まれたところ(textareaの子となるテキストノード)がデフォルト値となるので「th:text」を使う。

非表示でもフォームで送信

画面上に表示させないが、フォームで値を送信したい場合、type属性の値として「hidden」を指定する。(edit.htmlの14行目)

レコードの削除

レコードを削除するにはDELETEを使う。WHEREで更新するレコードを限定する。(Lecture11Controller.javaの110行目)

レコードの並び順

SELECTでレコードを取得するときには並び順を指定することができる。「ORDER BY カラム名 順番」を付加する。順番はASC(昇順)かDESC(降順)を指定する。(Lecture11Controller.javaの48行目)

改行を含む文字列の表示

HTMLにおいて改行はスペースと同様の扱いになる(基本的には。例外はpreタグ等)。このため改行がある場合は、brタグを挿入する等の処理が必要である。色々な方法が考えられるが、今回のコードでは下記のような処理を行なっている。

  • Note.javaにフィールドbodyを改行区切りのListで返すメソッドを追加(Note.javaの20〜33行目)
  • テンプレートでは1行づつ取得し、行末にbrタグを付加して表示(view.htmlの19〜21行目)

リンクの動的な生成

index.htmlでは操作に関するリンクを動的に生成している。

URLの一部を変数として扱う

マッピングの指定(@GetMapping、@PostMapping、@RequestMapping)で「{}」を使うと、その部分を変数として利用することができる。

Lecture11Controller.javaの117行目を見てみよう。ここでは「/view/{id}」となっている。これは、/view/1や/view/2といったURLに対応する。
「{id}」の部分を取得するには、メソッドの引数に、カッコ内と同じ文字列(ここではid)を指定して@PathVariableアノテーションを付加する(Lecture11Controller.javaの118行目)。

Spring Bootによるウェブアプリ開発: データベース

Spring Bootのバージョンアップ

Spring Bootが1.5.4にバージョンアップしているので自分のプロジェクトの利用バージョンもあげよう。

springBootVersionを書き換えて、プロジェクトを右クリックし、「Reload Project」を選択すると、Dependenciesなどが1.5.4のものに変更される。

永続化

本日の内容: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/html/boot-features-sql.html

永続化とは

これまで作ったウェブアプリは、起動し直すとそれまでに入力したデータ(例えば掲示板のコメント等)は消えてしまっていた。今回は、入力されたデータを保存することを考えよう。このように、プログラムの実行を終了してもデータが存続する特性を永続性(Persistence)といい、永続性を持たせることを永続化という。

Javaプログラムにおける永続化の方法

色々な方法がある。

  • テキストファイルとして保存
    • 独自の書式(フォーマット)を開発・利用
    • 既存のフォーマットを利用
      • CSV
      • HTML、XML(をベースとしたマークアップ言語)
      • JSON(JavaScript Object Notation)
      • YML
      • OpenDocument
      • etc…
  • バイナリファイルとして保存
    • 独自の形式を開発・利用
    • 既存のフォーマットを利用
      • Rich Text Format
      • PNG、JPEG、TIFF、MPEG
      • PDF
      • etc…
  • データベースシステムを利用
    • 独自のシステムを開発・利用
    • リレーショナルデータベースベース管理システム(relational database management system, RDBMS)を利用
    • RDBMS以外のデータベース(NoSQL、オブジェクトデータベース等)を利用
  • etc…

掲示板のデータをRDBMSを使って永続化する

本講義はJavaに関する講義であるが、RDBMSは知っておいた方がいい。実際、ウェブアプリ開発ではRDBMSがめちゃくちゃ使われている。

RDBMSはSQLという言語を使う。様々なRDBMSが存在するが、どれもSQLを使うので、これを知っておけばどのRDBMSでも基本的な操作はできるようになる。

Spring Bootでデータベースを使う準備

まずはRDBMSを選定しよう。今回は、組み込みで使える(アプリケーションの中に入れてしまうことができる)H2を使う。

次にJavaのプログラムからどのような方法を使ってデータベースにアクセスするかを決めよう。その方法は主に次の2つがある。

JDBCは、SQLをJavaプログラムの中で使ってデータベースを操作する方法で、JPAはO/Rマッパーというもので、Javaのインスタンスをデータベースに格納できる形に変換してデータベースにアクセスする方法(データベースにアクセスするSQLを自動生成する方法)である。

こう説明すると「JPAを使えばJavaだけでOK」と思えるかもしれないが、現状、JPAはSQLをよく知らないと使いこなすことはできない。

というわけで、本講義では、JDBCを使っていく。

build.gradleのdependenciesに下記の2行を追記して「Reload Project」を実行する(前回からの差分)。

これで、必要なファイル等が追加される。

ウェブインターフェースでデータベースに接続する

H2には、ブラウザからデータベースを操作できるインターフェースが内蔵されている。これはSpring Bootアプリケーションに組み込んだ状態でも利用することができる。

ウェブインターフェースを利用可能にするため、下記の1行をapplication.propertiesに加えよう。

bootRunで実行し、http://localhost:18080/h2-consoleにアクセスする。

h2-console

次に各設定項目を入力しよう。

  • JDBC URL: データベースの接続を表すURL。これにはどのディレクトリにデータを置くか、という情報が含まれる。今回は、ホームディレクトリに「webappdb」というディレクトリを作り、その中に「database」というファイル名で作るということを指定する。この場合、JDBC URLは「jdbc:h2:~/webappdb/database」となる(実際には「database.mv.db」等のファイルが作られる)。
  • User Name: データベースに接続する時のユーザ名を表す。今回はデフォルトのまま「sa」とする。
  • Password: データベースに接続する時のパスワードを表す。今回は「sa」とする。

入力して、Connectボタンをクリックすると、SQLの入力画面となる。

テーブルを作成する

掲示板のコメントを格納するためのテーブルを作成しよう。

次のSQL文を入力して、Runボタンをクリックするとテーブルが作成される。

「bbs_comment」はテーブル名。Javaのクラスに例えるとクラス名に相当する。

id、body、name、dateはカラム名。Javaのクラスに例えるとフィールド名に相当する。

INT、TEXT、TIMESTAMPは型で、それぞれJavaのint、String、Dateに対応している(参考: Data Types)。

AUTO_INCREMENTは、自動的に番号をつけることを意味している。だから、保存するときにidには値を入れないで良い。保存されたときに自動的に番号がつく。PRIMARY KEYは、そのレコード(Javaでいうところのインスタンス)における「テーブルの中に保存されているデータのある1行を識別するために必要な情報」を表し、主キーという。Javaにはそういうものはないが(無理やりこじつけるとすればhashCodeかな?)、データベースではレコードを区別するためにあったほうが良い(ほとんどの場合設定するが、なくても大丈夫)。

左側に表のアイコンがついた「BBS_COMMENT」というのができれば作成成功。

テーブルにレコードを登録する

登録するには、INSERT命令を使う。

うまくいったら、「Update count: 1」と表示される。適当に値を変えて、さらにデータを入力してみよう。

テーブルからレコードを検索・取得して表示する

データの検索・取得には、SELECTを使う。先ずは全件取得してみよう。

「*」は全てのカラムの情報(ここでは、id、name、body、date)を取得することを示す。

一部のデータを表示(検索)するには、WHERE節を追加する。

これは、nameカラムの値が「たろう」であるレコードだけが表示される。

プログラムから扱う

プログラムから扱うために、接続についての情報をapplication.propertiesに加えよう。ウェブインターフェースでデータベースに接続する時の情報と同じにする。

コントローラは次のようになる(GitHub)。
ベースとなっているLecture05Controllerと比較しながらみていこう。

24〜32行目

プログラムからJDBCを使いデータベースにアクセスするには、JdbcTemplateクラスを用いる。このクラスのインスタンスを用意し、データベースにアクセスするための各種メソッドを利用すれば良い。

この「JdbcTemplateクラスのインスタンス」をどのようにして用意すればいいだろうか?これまでと同様にnew演算子を使えばいいのだろうか?

Spring Bootの環境においては、一部のクラスは、自分でnew演算子を使ってインスタンス化するのではなくシステムにインスタンス化してもらって埋め込むという「依存性注入(dependency injection、DI)」によって行う。

JdbcTemplateクラスのインスタンスはDIで用意する。これを行なっているのが、コンストラクタである。コンストラクタに@Autowiredをつけると、引数はDIコンテナによってインスタンス化されたものが渡される。それをフィールドとして保持する。

52〜60行目

postされたデータをデータベースに登録する。

データを登録するINSERT文を実行するには、updateメソッドを利用する。

第1引数にSQL文を指定する。SQL文の中に「?」をかくと、そこには変数を埋め込むことができる。この「?」をプレースホルダ(placeholder)という。

第2引数以降はプレースホルダに対応した変数を指定する。

最後に、同一サーバの「/lecture10/bbs」にリダイレクトする。

34〜50行目

データベースからデータを全件取り出して表示する。

データを取り出すSELECT文を実行するには、queryForListメソッドを利用する。このメソッドの戻り値は、レコードのListである。レコードは、カラム名がキーでデータが値となるMapの形となっている。よって、queryForListの戻り値の型は「List<Map<String, Object>>」となる。

取り出したデータからBbsCommentのインスタンスを生成し、表示用のリストに入れていく。

拡張しよう(次回までの課題)

  • BbsCommentにIDを加え、IDも表示するようにしてみよう(第5回の時の掲示板を壊さないように、テンプレートを新たに作る等、工夫しよう)。
  • SQLのDELETE文を利用してデータベースから削除する機能を付け加えよう。

Spring Bootによるウェブアプリ開発: コレクション(2)

辞書を作る: Mapの利用

Mapとは

  • 「キー」と「値」をペアで扱うコレクション(Listは値だけを扱う)
  • キーはユニーク(unique、唯一の)になる。同じキーで違う値は登録できない。
  • キーと値を保存するにはputメソッドを使う。
  • キーで検索して値を取り出すにはgetメソッドを使う。
  • Mapはインターフェース。Mapを実装したクラスとしてはHashMapがよく使われる。
  • 教科書P.215〜227

Mapを利用する例として、次の機能を持つ辞書アプリを作ってみよう。

  • 英語をキー、日本語を値として登録できる。
  • 英語から日本語を検索できる。
  • 登録されたデータ一覧を見ることができる。

登録

  • コントローラ、22行目: 辞書の情報を格納するHashSetをフィールドとして定義する。
  • コントローラ、37行目: putメソッドでMapに登録する。

検索

  • コントローラ、55行目: getメソッドでキーから値を得る。登録されていない場合はnullが返ってくる。

一覧

  • テンプレート、24行目: keySetメソッドでキーのSetを取得する。Setについては教科書P.205〜214を参照。
  • テンプレート、26行目: getメソッドでキーから値を得る。

資料

ソースコード
オンラインドキュメント
教科書

  • Map: P.215〜227
  • Set: P.205〜214

Spring Bootによるウェブアプリ開発: コレクション(1)

Formの基礎(前回)のまとめ

前回のプログラム(おみくじ及び例1〜3)の動作を、下記の観点で確認しよう(記述の意味を理解すること)。

  • @RequestMappingアノテーション
  • @RequestParamアノテーション
  • ModelMap型のaddAttributeメソッド
  • postとgetの違い
  • @GetMappingアノテーションと@PostMappingアノテーション
  • th:text属性
  • th:each属性
  • th:if属性
  • @RequestParamアノテーションのrequiredオプション
  • メソッドのオーバーロード

掲示板の改良: 名前や日付も保存するList

第4回で作った掲示板はコメントだけを保存・表示するものであった。より実用的にするために、投稿者の名前と投稿時間も保存・表示するようにしてみよう。

まず第4回の掲示板を第5回で実行できるようにコピーする(テンプレートのパス等を修正しなければいけないことに注意する)。

どこをどのように変更するか?

  1. テンプレート: フォームに名前の入力欄を加える。
  2. コントローラ: 名前を受け取れるようにする。
  3. コメントを表すBbsCommentクラスを作成: 本文と名前と投稿日時のフィールドを作る。
  4. コントローラ: BbsComment型のインスタンスを作成
  5. コントローラ: コメントを保存するリストの型をList<BbsComment>にする。
  6. コントローラ: BbsComment型のインスタンスを作成し、リストに追加する。
  7. テンプレート: コメントの表示部分を修正する。

資料

ソースコード
オンラインドキュメント
教科書

  • Map: P.215〜227
  • Set: P.205〜214

Spring Bootによるウェブアプリ開発: Formの基礎

前回の要点

  • http://start.spring.ioでプロジェクトを作成する。
  • 処理の流れ
  • テンプレートとコントローラを作る。
  • テンプレート
    • HTMLで記述する。ただし単独で現れるタグは後ろに「/」をつける。
    • コントローラから送られてきた値を表示するところで、th:text属性を使う。
  • コントローラ
    • クラスに@Controllerアノテーションをつける。
    • クラスとメソッドにつける@RequestMappingアノテーションで担当するURLを指定する。
    • メソッドの返り値で利用するテンプレートを指定する。
    • テンプレートに値を送り込みたい場合は、メソッドにModelMap型の引数を加え、そのaddAttributeメソッドで名前と値を指定する。
  • その他
    • ポート番号を変える: resourcesにあるapplication.propertiesに「server.port = 18080」などと記述する。

ドキュメント

Formの作成と表示

おみくじアプリを作ってみよう。

処理のシーケンスは次のようになる。

おみくじのシーケンス

名前を入力する(フォームの利用)

  • formタグ: どこからどこまでがフォームかを表す。action属性でどこに送るか指定する。method属性でどのような形で送るかを指定する。
  • inputタグ: name属性で区別のための名前をつける。この例ではその名前にnameとつけて紛らわしいので注意すること。
  • buttonタグ: type属性をsubmitとすることで、formの送信ボタンにすることができる。

コントローラ

  • 18〜21行目:「/lecture04/omikuji_input」にアクセスがあると、「lecture04/omikuji_input」がブラウザに送られる。
  • 26〜34行目:「/lecture04/omikuji_output」にアクセスがあると、「lecture04/omikuji_output」がブラウザに送られる。
    • 27行目:「フォームから送られてくるパラメータを受け取るための引数」を指定する為に@RequestParamアノテーションをつける。
    • 28〜31行目: String型の変数を乱数によって「吉」か「大吉」にする。
    • 32行目: 「result」という名前で変数resultをテンプレートに送る。
    • 32行目: 「name」という名前で変数nameをテンプレートに送る。

結果の表示

  • コントローラからは「name」と「result」という名前で値が送られてきている。それぞれ、th:text属性で表示する。

例1: 掲示板

テンプレート

  • 最初の表示(get)とコメントを書き込む時(post)の両方でこのテンプレートを使う。
  • formのaction属性は省略すると、同じURLにアクセスすることになる。
  • リストから一つづつ取り出すのは、th:each属性を使う。コロンの左側でつけた名前でアクセスできる(スコープは、属性をつけた要素の内側)。注意しなければならないのは、繰り返し出力されるのは、th:each属性がついた要素(タグ)だということ。例えば、commentListに3つの文字列が入っていたとしたら、li要素が3つ出力される。
  • タグ無しで値を出力するのはth:blockタグを使う。

コントローラ

  • @RequestMappingはgetとpostの両方で実行されるが、@GetMappingと@PostMappingを使うことで、同じURLでgetとpostの処理を別のメソッドに分けて書くことができる。
  • コントローラ全体

例2: じゃんけん

テンプレート

  • 最初の表示(get)とじゃんけんをした時(post)の両方でこのテンプレートを使う。
  • th:ifで条件が真(true)の時だけ要素を表示させることができる。ここでは、コントローラからresultが送られてきた時(つまりresultがnullでない)にそれを表示している。
  • input要素のtype属性は「radio」。checked=”checked”をつけた要素がデフォルトで選択される。
  • name属性は全て同じにする(nameを同じにすると同一グループとなり排他的選択ができる)。
  • 選択されたボタンのvalue属性がコントローラに送られる。

コントローラ

  • getとpostで同じメソッド名となっているが、これをオーバーロードという(教科書P.132)。
  • StringBuilderは文字列を繋げる時に使える(教科書P.82)。
  • 律儀にコンピュータの手を決定して結果を出力してもいいが、ここでは選択した手に関わらずランダムで結果を出力している。
  • コントローラ全体

例3: アンケート

テンプレート

  • チェックボックスはname属性で区別する。

コントローラ

  • チェックボックスは、チェックをしたときにしかパラメータが送信されない。そのため、デフォルトの@RequestParamではエラーになる。
  • パラメータが送信されなくてもエラーにならないようにするには、「required = false」とする。
  • チェックされているときはtrue、チェックされてない時(パラメータが無い時)は、falseになる。
  • コントローラ全体

教科書

Spring Bootによるウェブアプリ開発: プロジェクトの作成からページの表示まで

プロジェクトの雛形を作成

  1. ブラウザで「http://start.spring.io」にアクセスする。
  2. 「Cradle Project」を選択する。
  3. グループIDとして、パッケージ名を入力する。ここでは「ユーザ名.webapp」とする。
  4. アーティファクトIDとしてプロジェクト名を入力する。ここでは「webapp」とする。
  5. 利用する依存ライブラリを入力する。名前の一部を入力すると選択肢が現れるのでクリックして選択する。ここでは「Web」と「Thymeleaf」を選択する。
  6. 選択されたものが表示されているので「Web」と「Thymeleaf」が選択されていることを確認する。
  7. クリックするとプロジェクトのファイル(ZIPファイル)のダウンロードが始まる。

start.spring.ioでプロジェクト作成

NetBeansでインポート

メニューから[ファイル]→[プロジェクトをインポート]→[ZIPから]を選択し、ダウンロードしたファイル(webapp.zip)を指定してインポートする。

インポート直後の構成

Spring Bootアプリケーションの実行

アプリケーションを実行するためのタスクが定義されているため、それを使う。

プロジェクトを右クリックして、[Task]→[boot]→[bootRun]を選択する。

アプリケーションが立ち上がると、出力ウィンドウの最後に「Started WebappApplication」と表示される。

その1行前に「Tomcat started on port(s): 8080 (http)」と出力されているということは、8080番ポートにアクセスすれば何らかの反応があるはずである。http://localhost:8080/にアクセスしてみよう。

サーバが出力したエラーページ

エラーメッセージが表示されたが、これは、まだコンテンツを返すプログラムを作っていないために表示されるもので、正常な動作である。アプリケーションを終了するには、出力ウィンドウの左側にあるStopボタンをクリックする。

Spring Web MVC frameworkの基礎中の基礎

処理の流れ

これから作るウェブアプリケーションはSpringのWeb MVCフレームワークを利用している。このフレームワークにおける処理の流れを次の図に示す(http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-servletより引用)。

Mvc

フロントコントローラがリクエストを適切なコントローラに渡し、コントローラが出力するデータをビュー(テンプレート)が表示するという仕組み。このうち、自分で作るのは、「コントローラ」と「ビューテンプレート」である。

まずは、単純に「こんにちは」と表示するようにコントローラとテンプレートを作ってみよう。

テンプレートを作る

テンプレートとは雛形のことであり、そこに流し込まれるデータとともに使うことが一般的な使い方であるが、まずはデータの流し込みを想定しないテンプレートを作成してみよう。

まず、テンプレートを格納するディレクトリを作成しよう。Resourcesのtemplatesにlecture03というディレクトリ(パッケージ)を作る。NetBeansのパッケージの表示の仕方によっては、作業が難しい。プロジェクトウィンドウの背景部分を右クリックし、「Javaパッケージの表示方法」を「ツリー」にすると階層構造がわかりやすい。

パッケージをツリーで表示

lecture03を右クリックして新規から「HTMLファイル」を選択する。ファイル名を「hello」と入力する。拡張子は自動でつくので入力しない。作成したら、次のように書き換えよう。

今の所、次の2点が注意する点で、これ以外は普通のHTMLファイルである。

  • 2行目: xmlns属性はXML名前空間の宣言である。テンプレートエンジンであるThymeleafはHTMLへの埋め込みをXMLによって行うことが大きな特徴である。名前宣言によってThymeleafによる操作をthというプレフィックスで行うことができる。
  • 4行目: 単独で使われるタグ(brやhr等)は後ろに/をつける(XMLのルール)。

コントローラを作る

コントローラは、リクエストに対してどのテンプレートを使うか(そしてテンプレートにどのような値を埋め込むか)ということを決定するためのものである。

具体的には@Controllerアノテーションをつけたクラスで、リクエストに対応してメソッドを定義する。メソッドの返り値としてテンプレートを指定する。

  • 12行目: コントローラを表すアノテーション
  • 13行目: このコントローラが、「/lecture03」のURLを担当することを表す。
  • 16〜19行目: /hello(つまりhttp://localhost:8080/lecture03/hello)に対応するメソッド
    • 16行目: /helloに対応すること表す
    • 17行目: 戻り値の型はStringにする。メソッド名はなんでも良い。
    • 18行目: テンプレートのルート(resources/templates)から利用するテンプレートの相対パス(拡張子は除く)

20行目以降は次節以降で解説する。

動作を確認

http://localhost:8080/lecture03/helloにブラウザでアクセスしよう。

テンプレートに値を渡す

  • メソッドにModelMap型の引数を加える。
  • そのaddAttributeメソッドで名前と値を登録する。
  • テンプレートでth:text属性を使う。

例1: サイコロ

アクセスするたびに表示される数が変化する。乱数については教科書P.108を参照。

例2: 挨拶

現在時刻によって表示されれる挨拶が変化する。時刻の扱いについては、教科書P.257を参照。