Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9140

Webの勉強はじめてみた その27〜モジュールごとの実装とデータベース〜

$
0
0
N予備校「プログラミング入門Webアプリ」を受講しています。 今回は第3章20節〜26節です。 承認されたユーザーだけが使える匿名掲示板の作成。 気をつけたい箇所や気付いた点だけをまとめました。 設計の進め方 システム要件の定義 UIの設計: ページに何をどう配置するか URIの設計: パスの作り方(RESTful) モジュールの設計 URIとモジュール リクエストを具体的な処理に振り分けることをルーティング、 リクエストに対し具体的な処理をする関数を リクエストハンドラという サーバーを起動するもの リクエストを処理するもの ルーティングを行うもの など、それぞれ役割を決めて実装する。 メインとなる機能から実装していくのが良い。 処理を実装する前にそれぞれの機能などをコメントする。 覚えておきたいこと テンプレートエンジンなどを使う場合、のちにhtmlになるものはviewsディレクトリに格納する。 以下はPOSTされた時の処理 // POSTの処理 let body =[]; req.on('data', (chunk) => { body.push(chunk); }).on('end', ()=>{ // クエリから値を取得 body = Buffer.concat(body).toString(); const params = new URLSearchParams(body); const content = params.get('content'); console.info(`投稿されました: ${content}`); }); querystringが非推奨になったのでURLSearchParamを使う。URIエンコードされた文字列のデコードも同時に行うので、decodeURIComponentはいらない。 認証機能 http-authモジュールの、ファイルを利用した認証 users.htpasswd admin:apple guest1:1234 guest2:5678 const basic = auth.basic({ realm: 'Enter username and password.', file: './users.htpasswd' }) Basic認証では、特定のURLにアクセスした際、ステータスコード 401 - Unauthorized を返すことでログアウトされる。 function handleLogout(req, res){ handleStatus(req, res, 401, 'ログアウトしました'); } function handleNotFound(req, res){ handleStatus(req, res, 404, 'ページが見つかりません'); } function handleBadRequest(req, res){ handleStatus(req, res, 400, '未対応のメソッドです'); } /** * HTTPレスポンスの共通処理 * @param {Object} req HTTP Request * @param {Object} res HTTP Response * @param {Number} statusCode * @param {String} msg Message */ function handleStatus(req, res, statusCode, msg){ res.writeHead(statusCode, { 'Content-Type' : 'text/plain; charset=utf-8' }); res.end(msg); } データベース 今回はPostgreSQLをsequelizeモジュールで操作。 docker-compose.yml services: app: depends_on: - db db: image: postgres:12 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: secret_board TZ: "Asia/Tokyo" docker-compose.ymlにdbというサービス名でPostgreSQLのコンテナを追加。 仮想環境上でappとdbの二つのサーバーが稼働する状態。 Sequelize yarn add sequelize@6.5.0 yarn add pg@8.5.1 yarn add pg-hstore@2.3.3 データベースの構成を設定することを、データモデリングという post.js // sequelizeの基本設定 const { Sequelize, DataTypes } = require('sequelize'); // データベース全体の設定 const sequelize = new Sequelize( // IDとパスワードを渡す 'postgres://postgres:postgres@db/secret_board', { // 起動ログなどのログを出力しない logging: false } ); // データモデリング const Post = sequelize.define( // テーブル名 'Post', { // データ定義 id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true }, content: { type: DataTypes.TEXT }, postedBy: { type: DataTypes.STRING }, trackingCookie: { type: DataTypes.STRING } }, { // テーブル名の固定 freezeTableName: true, // createdAt, updatedAtを自動追加 timestamps: true } ); // このファイルの起動時にデータベースの設定を同期する Post.sync(); // データモデリングしたオブジェクトをモジュールとして公開 module.exports = Post; insert const Post = require('./post'); Post.create({ //登録したいデータ }).then(() => { //登録後に実行してほしい処理 }) 非同期処理となる(Promiseオブジェクトが返ってくる)ので、thenメソッドを利用する。 コンソール上でのデータ確認 dbコンテナへ入る docker-compose exec db bash su postgres : ユーザーの変更(今回はpostgresというユーザー) psql : Postgres用コンソールに入る ¥c secret_borad : データベースに接続(今回はsecret_boardというデータベース) ¥q : 終了 select データの全件取得 // データの全件取得 Post.findAll({order:[['id', 'DESC']]}).then((posts) => { res.end( pug.renderFile('./views/posts.pug', { posts }) ); }); renderFileの引数に変数を指定することで、pugで参照可能となる。 delete destroyで指定したキーのデータを削除 投稿者自身か管理者のみが削除できるようにする function handleDelete(req, res){ switch(req.method){ case 'POST': let body = []; req.on('data', (chunk) => { body.push(chunk); }).on('end', () =>{ body = Buffer.concat(body).toString(); const params = new URLSearchParams(body); const id = params.get('id'); Post.findByPk(id).then((post) => { if(req.user === post.postedBy || req.user === 'admin'){ post.destroy().then(() => { handleRedirectPosts(req, res); }) } }) }) break; default: util.handleBadRequest(req, res); break; } } curlなどで第三者がdeleteを送る可能性があるため、サーバー側でもユーザーのチェックをすること。 実行するとデータベースから実際にデータを削除することを物理削除、実際にデータを削除する代わりに「削除された」ことを表すフラグを立ててデータが削除されたとみなすことを論理削除という。 削除処理は慎重に。実用的にも論理削除が良い。 データベースの永続化 dockerを破棄するとデータも破棄されるので、PC側の別の場所にファイルを保存する。 docker-compose.yml db: image: postgres:12 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: secret_board TZ: "Asia/Tokyo" volumes: - ../secret-board-db:/var/lib/postgresql/data PC側: ../secret-board-db docker側: var/lib/postgresql/data docker起動時に同期させる トラッキングCookie ユーザーの行動を追跡するために付与される Cookie のことをトラッキング Cookie と呼ぶ。 cookiesというnpmモジュールを利用する yarn add cookies@0.8.0 const Cookies = require('cookies'); const trackingIdKey = 'tracking_id'; // TrackingCookie const cookies = new Cookies(req, res); addTrackingCookie(cookies); function addTrackingCookie(cookies){ // Cookieが存在しない場合 if(!cookies.get(trackingIdKey)){ // ランダムな数値を登録 const trackingId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); // 有効期限を翌日(24時間後)まで const tomorrow = new Date(Date.now() + (1000 * 60 * 60 * 24)); cookies.set(trackingIdKey, trackingId, {expires: tomorrow}); } } MAX_SAFE_INTEGER : JavaScriptで扱える最大値 PUG側 each post in posts - let isPostedAdmin = (post.postedBy === 'admin') if isPostedAdmin h3 #{post.id} : 管理人 ★ else h3 #{post.id} : ID:#{post.trackingCookie} p!= post.content p 投稿日時: #{post.createdAt} - let isAdmin = (user === 'admin') if isAdmin p 投稿者: #{post.postedBy} - let isDeletable = (user === post.postedBy || isAdmin) if isDeletable form(method="post" action="/posts?delete=1") input(type="hidden" name="id" value=post.id) button(type="submit") 削除 hr each in  渡されてきたコレクションをループ。 - pugテンプレート内にJavaScriptを直接記述することができる。 p!= post.content pugテンプレート内で文字列に含まれたタグを認識させる。 まとめ モジュールごとの実装、わかりにくかったのは名前のせいもあるのかな。 formからのPOSTメソッド、データベースのPost、データベースモデリングのpost.js、URIクエリのposts。 データベースからの削除は、なるほどと思った。 実際に削除するとなるとほかのテーブルも意識しないといけないので、実務的にも慎重になる事案。 しかし設計の重要性というのをつくづく感じる今日この頃。

Viewing all articles
Browse latest Browse all 9140

Trending Articles