20200804のNode.jsに関する記事は7件です。

toJSONを用いて、JSONデータの返り値を制御する

JSONデータの返り値を制御する

例)petというJSONデータの場合

const pet = {
   name: 'Doggy'
}

console.log(JSON.stringify(pet))


{'name':'Doggy'}
というJSONデータが返ってくる。

★返り値となるJSONデータを、toJSONを使って制御することができる

const pet = {
   name: 'Doggy'
}

pet.toJSON = function(){
  return {}
}

console.log(JSON.stringify(pet))


{}
というJSONデータが返ってくる。

つまり

toJSONで上書きされた値が、pet定数のJSONデータとして認識される

データベースのセキュリティ向上に利用

//mongooseを利用して、データベース型を設定
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
    name: {
      type: String,  //データ型の設定
      required: true,
      trim: true
  },
  password:{
      type: String,
      required: true,
  },
  tokens: [{
    token: {
      type: String,
      require: true
    }
  }],
 })

//toJSONを用いて、外からuserObjectにアクセスした時に、返すメソッドと返さないメソッドを決める
userSchema.methods.toJSON = function(){
  const user = this
  const userObject = user.toObject()

  delete userObject.password
  delete userObject.tokens


  return userObject
}


これで、userObjectは、toJSONで指定されている、passwordとtoken以外のデータを返すようになる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bcryptでプレーンテキストをハッシュ化させる

ハッシュ化のメリット

・パスワードなどをデータベースに保存する時、プレーンテキストのまま保存すると脆弱性に繋がる。

・ハッシュ化したメッセージダイジェストから、元のメッセージを復元することは困難
→「インクリプション」…元のメッセージをとっておいて、それをハッシュ化したら同じハッシュ値になるかチェックすることで、同一性を確認する仕組み

bcryptパッケージ

bcryptというnpmパッケージを利用すると、プレーンテキストをハッシュ化することができる。

・bcryptパッケージの使い方
const bcrypt = require('bcryptjs')

const myFunction = async() => {
const password = "password1234"

const hadhedPassword = await bcrypt.hash(password, 8) //第一引数:ハッシュ化したい値、第二引数:roundをかける回数。公式推奨は8回

console.log(password) //元のパスワードを表示
console.log(hassedPassword) //ハッシュ化されたパスワードを表示

//元のパスワードとハッシュ化されたパスワードを比較し、同じものであるか確認

const isMarch = await bcrypt.compare('password', hashedPassword)

}

myFunction()

値をハッシュ化させてからデータベースに組み込む

●Middlewareを利用してMongooseの振る舞い方をカスタマイズできる

Mongoose公式ドキュメント
「Middleware > Save/Validate」の項目
スクリーンショット 2020-08-04 22.35.59.png

・validateする前、または後にイベントを設定できる。
・saveする前、または後にイベントを設定できる。

→save()する前に、データベースに保存したい値をハッシュ化させることができる。

●例)
データベースに格納する前にpasswordをハッシュ化

const mongoose = require('mongoose')
const bcrypt = require('bcryptjs') 

const userSchema = new mongoose.Schema({
    name: {
      type: String,  //データ型の設定
      required: true,
      trim: true
  },
  password:{
      type: String,
      required: true,
      }
  })

//middlewareを記述。saveする前にプレーンテキストをハッシュする
userSchema.pre('save', async function(next){ //アローファンクションは使わない。このbindingは大事な役割を果たすものだから
  const user = this

  console.log('Middleware is working')//ミドルウェアが動いているか確認するためのテストコード

  if(user.isModified('password')){ //ハッシュ化されていないパスワードがある時
    user.password = await bcrypt.hash(user.password, 8)
  }//ここで値がハッシュされる

  next()
})

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】kuromoji.js + mecab-ipadic-neologdで形態素解析して遊ぶ

スクレイピングした文章を形態素解析したい!

nodeではjava用に書かれたkuromojiを移植したkuromoji.jsを使用するのが簡単っぽい。(自分調べ)

しかしWeb上の文字は日々変化し、思ったように解析できない場合も多い。
そこでWeb上の資源を解析して作られている新語・固有表現に強いmecab-ipadic-NEologdという辞書も導入する。
(+ベースはipadic辞書)

環境

  • Windows 10, git-bash, MINGW64(素直にlinux使ったほうが簡単です)
  • busybox(UNIXコマンド群、後で導入)
  • node v12.18.3
  • npm v6.14.7
  • kuromoji.js v0.1.2

kuromoji.js の導入

kuromoji.jsにはデフォルトで辞書が入っているため、使うだけなら非常に簡単。
サンプルのdicPathを、node_module内の辞書を参照するように変更する。

$ npm install kuromoji
sample.js
var kuromoji = require("kuromoji");

kuromoji.builder({ dicPath: "node_modules/kuromoji/dict" }).build(function (err, tokenizer) {
  // tokenizer is ready
  var path = tokenizer.tokenize("すもももももももものうち");
  console.log(path);
});

実行すると形態素解析した結果が出力されるはず。

kuromoji-js-dictionary の導入

kuromoji.jsに新しい辞書を追加するには、その辞書をkuromoji用に変換する必要がある。
これを支援してくれるスクリプトがあったので使用させてもらう。

$ git clone https://github.com/sable-virt/kuromoji-js-dictionary.git

$ cd kuromoji-js-dictionaly
$ npm install

windows環境では一部動かないので手作業で修正...

package.json
{
  "scripts": {
+   "euc": "find ./neologd-seed -name '*.csv' -exec sh -c 'nkf -e --overwrite {}' ';'",
+   "tar": "find ./neologd-seed -name '*.csv' -exec sh -c 'tar -cvzf ./dict/neologd/$(basename {}).tar.gz {}' ';'",
-   "tar": "find ./neologd-seed -name '*.csv' -exec sh -c 'tar -cvzf ./dict/neologd/$(basename '{}').tar.gz {}' \\;",
  }
}

eucはgitのログを見る限り必要無くなったようだが、npm run convertから消し忘れている様子...。
必要ないか断定できないのでログからリバートした。
また-execコマンドはwindowsでは;で終わらせる必要があるらしいので修正。

ビルドするために他にxznkfが必要なので、無い場合は適当に用意しておく。
linux環境ならbrewなどで適当に取ってこれるがwindowsだとこれまた厄介。

xzbusyboxを使用し、nkfvictorから適当なものを用意した。
それぞれ--helpコマンドが通れば追加完了。
その他足りないコマンドは各自で。

辞書のコンバート

ここからはレドメ通りなのだが一応記録しておく。

./neologd-seedのディレクトリに使用したい辞書(****.csv.xz)を置いておく。
初めから以下のの辞書が置いてあるので、とりあえずそれを使用する。

  • ../dict/mecab-ipadic-2.7.0-20070801.tar.gz (ベースの辞書)
  • mecab-user-dict-seed.20180322.csv.xz
  • neologd-adjective-verb-dict-seed.20160324.csv.xz
  • neologd-common-noun-ortho-variant-dict-seed.20170228.csv.xz
  • neologd-noun-sahen-conn-ortho-variant-dict-seed.20160323.csv.xz
# ./dict/neologd に変換した辞書を出力
$ npm run convert

# ./dist に kuromoji.js 用の辞書を出力
$ ./bin/run

All done!!って言われたら完成!
./dictディレクトリを自分のプロジェクトに持ってきて、dicPathを変更すれば完成。

TypeError: Cannot read property 'lookup' of nullは辞書が見つからないエラー。
おま環か分からないが、package.jsonからの相対パスで記載すればOKだった。

新しい辞書の追加(mecab-ipadic-NEologd)

上記では予め用意された辞書を使用したが、NEologdはビルドすることで最新の辞書を作成することができる。(週2更新?)
ただそれはまた大変なので、リポジトリに定期的に保存?されているビルドされた辞書を使用する。
上記URLの ./seed からビルドされた辞書を取得できる。

試しにmecab-user-dict-seed.****.csv.xzという辞書を追加する場合...
この辞書をダウンロードして、./neologd-seedに放り込む。
前回のビルドの残骸(***.csv)が残っている場合、消しておいてもよい。
その後、上記の「辞書のコンバート」の章と同じことをやれば完成だ。

他の辞書もこれと同様の手法で導入できる(はず)。

NEologdの効能

NEologdを今試しているサンプルに適用してみた。
これはある動画のチャット欄から単語として有用な名詞、動詞、形容詞を抽出し、出現数をカウントしたものである。
(ちなみにあんかけパスタを食べているシーンのチャット200件を対象としている)

未適用の状態と比較すると「名古屋めし」や「あんかけパスタ」といういかにもな単語が、ちゃんと1ワードになっていることが確認できた。
2018年版と2020年版は特異な単語が無かったせいか大きな変化は無かった。
よって、Webの情報を処理する際には、NEologdを使用したほうが効果的であろう。

neologd_test_diff.png

結論

kuromojiNEologdを使ってどんどん形態素解析してこ!

あとがき:

nodeでゴリゴリ書けるのは非常に助かる。
やろうと思えばブラウザでも実行できるのは強い。
Sudachiってのも試してみたいね。

ぶっっっちゃけスクリプトをnodeで書き直せばこんなに引っかからないよねぇ。
けど、元ソースを読み解くのも面倒なので、環境側を合わせる方針にした。

...WSLを使うのも手か?(まだ触ってないから未知の世界)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IBM Cloudのメール配信サービス(SendGrid)を使ってNode.js、Pythonからメール配信してみた!

背景

現在私が担当している案件で一般ユーザー向けのサービスを提供する予定で、ユーザー登録をした際などにメールをユーザーに送信する必要があるとのことで、メール送信サーバーをどうやって調達するかが課題となった。
お客様の方で用意できるメール送信サーバーで使えるものはないということだったので、IBM Cloud内で使用可能なサービスがないのかを調査した結果以下のサービスが利用できそうなので、実際に使えるのかどうか、利用方法はどんな感じかを実際にサービス登録して使ってみたのでこの記事にまとめました。
https://cloud.ibm.com/docs/email-delivery?locale=ja

E メール配信について
SendGrid の IBM Cloud E メール配信サービスは、メール・リレー・サービスです。このサービスにより、スマート・ホストを使用してアウトバウンド・メール・サービスのリレーが可能になります。スマート・ホストは、SMTP サーバー、メール・クライアント、または SMTP を処理可能な任意のサービスまたはプログラム言語からの SMTP トラフィックをリレーします。 また、このサービスでは、メトリックの生成、E メール・リストの追跡、E メール・アクティビティー (E メールのバウンス、クリック、ドロップ、オープンなど) の追跡も行います。 このサービスには、ニュースレター支援や認証などの他の機能も用意されています。

※なお、マニュアルには何が出来るかほとんど記載されていませんが、サポートに問い合わせたところ、IBM Cloud経由でSendGridに作成されたアカウントで選択したアカウント・タイプで使用可能なSendGridの機能はそのまま利用できるとのことです。

登録方法

  1. https://cloud.ibm.com/classic/services/emaildelivery にアクセスし、Order Email Delivery Serviceのボタンをクリックする
    screencapture-cloud-ibm-classic-services-emaildelivery-2020-07-15-16_25_22.jpg

  2. Account Typeを選択し、Email AddressUsernamePasswordを設定し、Continueボタンをクリックする
    ※UsernameとPasswordはSendGridアカウントのユーザー名、パスワードとして使用される。
    screencapture-cloud-ibm-classic-services-emaildelivery-2020-07-16-10_31_17.jpg

  3. 登録が完了し一覧に表示されたら、ActionsよりAccess Vendor Portalを選択し、Send Gridのポータルにアクセスする
    2020-08-04 14_56_27-IBM Cloud __ Email Delivery Service.jpg

  4. 手順3で設定したUsernameとPasswordでログイン出来ることを確認する。
    screencapture-app-sendgrid-login-2020-07-15-16_55_32.jpg

使い方

  1. SendGridのポータルからSetup Guideを開く
    screencapture-app-sendgrid-2020-08-04-15_04_17.jpg

  2. Integrate using our Web API or SMTP Relay横のStartを選択する
    screencapture-app-sendgrid-guide-2020-08-04-15_04_34.jpg

  3. Web API以下のChooseを選択する
    screencapture-app-sendgrid-guide-integrate-2020-08-04-15_04_41.jpg

  4. 使用した言語横のChooseを選択する
    screencapture-app-sendgrid-guide-integrate-langs-2020-08-04-15_04_54.jpg

  5. My First API Key Name配下にAPIキー名を入力した上で、Create Keyボタンをクリックする
    screencapture-app-sendgrid-guide-integrate-langs-nodejs-2020-08-04-15_05_32.jpg

  6. 後は画面に表示された手順に乗っ取り該当のコードを実装し、実行すればメールが送信される
    screencapture-app-sendgrid-guide-integrate-langs-nodejs-2020-08-04-15_30_02.jpg

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのAngular入門ガイド

Angularプロジェクトの概要と始め方を記載します。Versionの変更がたびたびあるので、内容はリンクは最新のVersionのページを確認すること。

社内教育に使っている資料なのでメモ書きです。

URL

Angular ドキュメント

Angular 基本概念

Angular チュートリアル

アプリケーション生成

ng new application-nameでプロジェクト構成
- Routingの設定を聞かれるので > y
- cssの設定は scss

起動コマンド

ng serve

http://localhost:4200で画面

ディレクトリ構成

全体感をつかむためにプロジェクトの構造を把握します。ディレクトリ構造を知っておくとバグ時にどのファイルが原因か可能性を絞れるようになります。

コンパイル関係

tsconfig

tsconfig.json: editorから参照されるファイル? (詳細は理解しておらず)
tsconfig.base.json: app.json, spec.jsonの両方に継承されているコンパイル設定
tsconfig.app.json: 通常のコンパイル設定
tsconfig.spec.json : テスト用のコンパイル設定

angular.json

Angularプロジェクトのビルドの設定などが記述されている。よく利用して重要なのは以下の3プロパティー。

  • assets: 画像などを格納するdirectory
  • styles: cssやscssなどを格納する。node_modulesに入っているscssなどを入れる。
  • scripts: Javascriptファイルを読み込む
"assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": ["node_modules/libraryA/js/index.js"]

おそらく最適化などの処理をしてもらえるので、Index.htmlをいじるのでなく上のファイルをいじることでJavaScriptやCSSなどを読み込むのがよい。

その他

とりあえずデフォルトのままで問題ないファイル。

src/test.s
src/main.ts
src/polyfills.ts
src/index.html

エディター設定ファイル

.editorconfig: エディター一般の構文設定
tslint.json: typescriptの構文設定

環境設定ファイル

angular.jsonファイルのコンパイル設定で設定だれている。

envrionments.environment.ts

Angularの構文

特に重要で理解したい概念

  1. Component
  2. Provider(Service)
  3. Module

理解したい構文

以下のdeclarations, imports, providers,exports。bootstrapはとりあえずとばしてよい

@NgModule({
  declarations: [
    AppComponent,
    UserListComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  exports: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

とりあえずすすめたいところ

Getting Started, FundamentalsのComponent Interactionまで

image.png

モジュールとコンポーネント

export, import, declarationとcomponentの関係を正確に理解するのが重要


Yet to be written.

モジュールとコンポーネントの関係を理解するためのデモソースコード
URL: https://github.com/programmerkgit/angular-module-import-demo

Angular Module

  1. Moduleのdeclareに記載されたComponentが利用できるようになる。
  2. componentは同じmodule内でdeclareされた他のcomponentを利用できる。
  3. componentは同じmodule内にimportされたModuleの中でdeclareかつexportされたcomponentを利用できる
  4. MainModuleがChildModuleをimportし、ChildModuleがGrandChildModuleをimportしている場合。ChildModuleがGrandChildModuleをexportすると、MainModuleからGrandChildModuleでexportしているModuleやcomponentにアクセスできる
MainModule
  declaration: [MainComponentA, MainComponentB],
    import: [ChildModule]

ChildModule
    declaration: [ChildComponent, ExportedChildComponent]
    import: [GrandChildModule, ExportedGrandChildModule],
  export: [ExportedGrandChildModule]

GrandChildModule
    declaration: [GrandChildComponent, ExportedChildComponent]
  export: [ExportedChildComponent]

ExportedGrandChildModule
    declaration: [ExportedGrandChildComponent, ExportedExportedChildComponent]
  export: [ExportedChildComponent]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでtmpに一時ディレクトリを作るにはfs.mkdtempSyncを使う

Node.jsで/tmpに一時的なディレクトリを作るには、fs.mkdtempSyncを使います。

const fs = require('fs')

const dir = fs.mkdtempSync('/tmp/foo')

console.log(dir)
//=> "/tmp/foo4xeXpJ"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub APIのレスポンスが 403 Forbidden になったら

User-Agentヘッダーは設定していますか??
https://docs.github.com/en/rest/overview/resources-in-the-rest-api#user-agent-required

コード例

以下はissueを作成するコードです。
認証はパーソナルアクセストークンで行います。

PersonalAccessToken, organization, repositoryはご自身のものに置き換えてください。

github.ts
import https from 'https'

const createIssue = async (title: string, body: string, labels: string[]): Promise<number> => {
  return new Promise((resolve, reject) => {
    const req = https.request({
      method: 'POST',
      headers: {
        'User-Agent': 'YourApp', // これ大事
        'Accept': 'application/vnd.github.v3+json',
        'Content-Type': 'application/json',
        'Authorization': `token ${PersonalAccessToken}`
      },
      host: 'api.github.com',
      path: `/repos/${organization}/${repository}/issues`,
    }, (response) => {
      let res: any = ''

      response.on('data', (chunk) => {
        res += chunk
      })

      response.on('end', () => {
        res = JSON.parse(res)
        resolve(res.id)
      })

      response.on('error', (err: Error) => reject(err))
    })
    req.write(buildPayload(title, body, labels))

    req.on('error', (err: Error) => reject(err))
    req.end()
  })
}

const buildPayload = (title: string, body: string, labels: string[]): string => {
  return JSON.stringify({
    title, body, labels
  })
}

まとめ

全ての原因がこれに当てはまるかは分かりませんが、私はこれで解決できました。
@octokit/rest.jsなどを使った方が早いかもしれませんね)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む