クラサバ通信部の作成

  • Angular5で「俺式KOANスタック」でWebアプリ構築するための個人的な備忘録記事。
  • 概要については「こちら」を参照。
  • 事前に「バックエンド初期設定」が完了していることが前提。
  • 本記事まで完了すればWebアプリとして最低限の動作が可能

中間部分を作成

クライアントとサーバでやり取りするインターフェースや、どちら側でも使用できる様なモジュール領域を定義します。
普通はそんな構成にしないかもしれませんが、その辺は「俺式KOANスタック」ということで。

フォルダ および ファイル を追加

  • ルートディレクトリ直下に middle フォルダを作成し、配下に exchange, base, sample新規に空のファイル request.ts, response.ts, sample.request.ts, sample.response.ts を作成して以下のような構成にする
middle
 └─ exchange
      ├─ base
      │   ├─ request.ts
      │   └─ response.ts
      │
      └─ sample
          ├─ sample.request.ts
          └─ sample.response.ts

request.ts を記述

middle/exchange/base/request.ts を以下の通りにする

middle/exchange/base/request.ts
/** 基底リクエスト */
interface IRequest {
    requestId: string;
}
export = IRequest;

response.ts を記述

middle/exchange/base/response.ts を以下の通りにする

middle/exchange/base/response.ts
/** 基底レスポンス */
interface IResponse {
    status: boolean;
    statusCode: number;
}
export = IResponse;

sample.request.ts を記述

middle/exchange/sample/sample.request.ts を以下の通りにする

middle/exchange/sample/sample.request.ts
import Request = require('../base/request');

/** サンプル リクエスト */
interface ISampleRequest extends Request {
    num1: number;
    num2: number;
}
export = ISampleRequest;

sample.response.ts を記述

middle/exchange/sample/sample.response.ts を以下の通りにする

middle/exchange/sample/sample.response.ts
import Response = require('../base/response');

/** サンプル レスポンス */
interface ISampleResponse extends Response {
    result: number;
}
export = ISampleResponse;

サーバ側を対応

MiddlewareRouter を修正

server/middleware.router.ts を以下の通りに修正する

server/middleware.router.ts
import KoaRouter = require('koa-router');

import Request = require('../middle/exchange/base/request');
import Response = require('../middle/exchange/base/response');
import SampleContoroller = require('./app/controller/sample.controller');

/** ミドルウェア ルータ */
class MiddlewareRouter {
    /** 各ルータを束ねたミドルウェアを生成 */
    public routes(): KoaRouter.IMiddleware {
        const koaRouter = new KoaRouter();

        koaRouter.get('/api/get-sample', this.callGet(new SampleContoroller().test));
        koaRouter.post('/api/post-sample', this.callPost(new SampleContoroller().test));

        return koaRouter.routes();
    }

    /**
     * 実行処理の呼び出し(GET)
     * @param controll 実行される処理
     * @returns ルータに設定するミドルウェア
     */
    private callGet(controll: (request: Request) => Promise<Response>): any {
        return async function(ctx: KoaRouter.IRouterContext) {
            const ctxRequest: any = ctx.request;
            const ctxResponse = await controll(ctxRequest.query);
            ctx.body = ctxResponse;
        };
    }

    /**
     * 実行処理の呼び出し(POST)
     * @param controll 実行される処理
     * @returns ルータに設定するミドルウェア
     */
    private callPost(controll: (request: Request) => Promise<Response>): any {
        return async function(ctx: KoaRouter.IRouterContext) {
            const ctxRequest: any = ctx.request;
            const ctxResponse = await controll(ctxRequest.body);
            ctx.body = ctxResponse;
        };
    }
}
export = MiddlewareRouter;

ルート「/」へのGETリクエストメソッドを削除

ベタ書きのインターフェースを削除し、作成したインターフェースを利用するように変更

POSTリクエストの処理を追加

SampleController を修正

server/app/controller/sample.controller.ts を以下の通りに修正する

server/app/controller/sample.controller.ts
import SampleRequest = require('../../../middle/exchange/sample/sample.request');
import SampleResponse = require('../../../middle/exchange/sample/sample.response');

/** サンプル コントローラ */
class SampleContoroller {
    public async test(request: SampleRequest): Promise<SampleResponse> {

        console.log('=== SampleContoroller # test ===');
        console.log(request);

        return await {
            status: true,
            statusCode: 200,
            result: 1
        } as SampleResponse;
    }
}
export = SampleContoroller;

「any」型のインターフェースをやめ、作成したインターフェースを利用するように変更

server.ts を修正

server/server.ts を以下の通りに修正する

server/server.ts
// アプリケーションサーバ起動設定
import Koa = require('koa');
import path = require('path');
import koaStatic = require('koa-static');
import bodyparser = require('koa-bodyparser');
import MiddlewareRouter = require('./middleware.router');

const app = new Koa();

// サーバ側ミドルウェアの提供
app.use(bodyparser({
    extendTypes: {
        json: ['application/x-javascript']
    }
}))
.use(new MiddlewareRouter().routes());

// クライアント側ミドルウェアの提供
app.use(koaStatic(path.resolve(process.cwd(), './dist/client')));

// サービス開始
const port = 3000;
app.listen(port, () => console.log(`App is listing port in ${port}`));

クライアント側ミドルウェアの提供処理を追加

フロントエンド側を対応

sample.service.ts を修正

client/app/services/sample.service.ts を以下の通りに修正する

client/app/services/sample.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import 'rxjs/add/operator/toPromise';

import SampleRequest = require('../../../../middle/exchange/sample/sample.request');
import SampleResponse = require('../../../../middle/exchange/sample/sample.response');

@Injectable()
export class SampleService {

  constructor(private http: HttpClient) { }

  /** GET通信 サンプル */
  public async getReq(): Promise<any> {
    // GETリクエスト作成
    const httpHeaders: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
    let httpParams: HttpParams = new HttpParams();
    httpParams = httpParams.append('testKey', 'aaaaa');
    httpParams = httpParams.append('otherKey', 'bbbbb');
    httpParams = httpParams.append('testKey', 'ccccc');

    // GETリクエスト送信
    return await this.http.get<SampleResponse>('/api/get-sample', {
      headers: httpHeaders,
      params: httpParams
    }).toPromise();
  }

  /** POST通信 サンプル */
  public async postReq(): Promise<any> {
    // POSTリクエスト作成
    const httpHeaders: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
    const body = {
      num1: 7,
      num2: 9
    } as SampleRequest;

    // POSTリクエスト送信
    return await this.http.post<SampleResponse>('/api/post-sample', body, {
      headers: httpHeaders
    }).toPromise();
  }
}

GET/POSTリクエスト通信を非同期で行うように変更

コンポーネント修正

sample.module.ts 修正

client/app/components/sample.module.ts を以下の通りに修正する

client/app/components/sample.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';

import { SampleComponent } from './sample.component';
import { SampleServiceModule } from '../../services/sample/sample-service.module';

/**
 * サンプル コンポーネントモジュール
 */
@NgModule({
    imports: [
        CommonModule,
        RouterModule,
        FormsModule,
        SampleServiceModule
    ],
    declarations: [
      SampleComponent,
    ],
    exports: [
      SampleComponent
    ]
})
export class SampleModule {}

修正したサービスのモジュールをインポート

sample.component.ts 修正

client/app/components/sample.component.ts を以下の通りに修正する

client/app/components/sample.component.ts
import { async } from '@angular/core/testing';
import { Component, OnInit } from '@angular/core';

import { SampleService } from '../../services/sample/sample.service';

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.css']
})
export class SampleComponent implements OnInit {

  constructor(private service: SampleService) { }

  ngOnInit() {
  }

  /** TEST GET ボタン押下時 */
  async testGetBtn(): Promise<void> {
    const res = await this.service.getReq();
    console.log('\n=== res ===');
    console.log(res);
  }

  /** TEST POST ボタン押下時 */
  async testPostBtn(): Promise<void> {
    const res = await this.service.postReq();
    console.log('\n=== res ===');
    console.log(res);
  }
}

作成したGET/POSTリクエストのサービスを呼ぶように変更

sample.component.html 修正

client/app/components/sample.component.html を以下の通りに修正する

client/app/components/sample.component.html
<div>
  sample works!
  <button (click)="testGetBtn()">TEST GET</button>
  <button (click)="testPostBtn()">TEST POST</button>
</div>

GET/POSTリクエストのサービスを呼ぶボタンを追加

クラサバ通信確認

動作確認 (ts-node起動)

  • npm run start でサーバ(server.ts)を起動し、コンソール上に「App is listing port in 3000」と表示されたら、Webブラウザで http://localhost:3000/#/sample-page にアクセスし、Webブラウザの開発者モード(ChromeならF12など)を起動し、ブラウザコンソールを表示する。
    • 画面最下部の「TEST GET」ボタンを押下し、ブラウザコンソールに「{status: true, statusCode: 200, result: 1}」と表示され、かつ、コンソール上に以下のとおりにレスポンスが表示されたら成功。
=== SampleContoroller # test ===
{ testKey: [ 'aaaaa', 'ccccc' ], otherKey: 'bbbbb' }
  • 画面最下部の「TEST POST」ボタンを押下し、ブラウザコンソールに「{status: true, statusCode: 200, result: 1}」と表示され、かつ、コンソール上に以下のとおりにレスポンスが表示されたら成功。
=== SampleContoroller # test ===
{ num1: 7, num2: 9 }
  • 動作確認できたら Ctrl + C でサーバ終了。

動作確認 (node起動)

  • 事前に「dist」フォルダを削除し、コンソール上で npm run build コマンドでビルドを実行し、dist フォルダ配下に「client」「middle」「server」フォルダと配下のファイルが生成されていたら準備完了。
  • node dist/server/server.js でコンパイルされたサーバ(server.ts)を起動し、コンソール上に「App is listing port in 3000」と表示されたら、Webブラウザで http://localhost:3000/#/sample-page にアクセスし、Webブラウザの開発者モード(ChromeならF12など)を起動し、ブラウザコンソールを表示する。
    • 画面最下部の「TEST GET」ボタンを押下し、ブラウザコンソールに「{status: true, statusCode: 200, result: 1}」と表示され、かつ、コンソール上に以下のとおりにレスポンスが表示されたら成功。
=== SampleContoroller # test ===
{ testKey: [ 'aaaaa', 'ccccc' ], otherKey: 'bbbbb' }
  • 画面最下部の「TEST POST」ボタンを押下し、ブラウザコンソールに「{status: true, statusCode: 200, result: 1}」と表示され、かつ、コンソール上に以下のとおりにレスポンスが表示されたら成功。
=== SampleContoroller # test ===
{ num1: 7, num2: 9 }
  • 動作確認できたら Ctrl + C でサーバ終了。

ここまでが クラサバ間の通信部分を構築。
これで、Webアプリとして最低限の動作が可能なので、どっかのサーバにおいてWebブラウザから叩くと使えます。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.