LoginSignup
8
7

More than 5 years have passed since last update.

サブディレクトリ URL で起動する Angular の Web アプリで API 通信を実装したらハマった話+解消手順

Posted at

概要

Angular (+ express) + Node.js でバックエンドに API 通信をさせた時にエラーでハマった経緯と、その解消策の備忘録

先に結論から言うと

「Nginx の設定をミスったら、バックエンド通信に失敗するよ」と言う話。

経緯

Angular + express で MEAN スタックで作成した Web アプリ。
今までは普通に、開発 ➡️ サーバへデプロイ ➡️ 実機で動作確認、で問題なく動いていた。
ただし、ドメイン取得して、サーバのトップレベルで動作する Web サイトとして作っていた。

今回は、サブディレクトリでのみ動作する Web サイトとして開発。
つまり...

  • https://example.com ▶︎ 別の Web サイト(静的 HTML ページ)
  • https://example.com/my-app ▶︎ 今回の Angular サイト

としようとした。
実際に、Angular でアプリを開発して配置。

事象と解消手順

事象1: 画面表示でエラー発生

https://example.com/my-app にアクセスしたら、テキストだけ出るけど装飾系が描画されない。と言う中途半端な状態で画面表示。表示が壊れている状態。。。
もちろん、https://example.com は問題なく規定の Web ページが表示される。

▶︎ 解消: 「base href」を設定したら解消

index.html の <base href="/"><base href="/my-app/"> となると解消する。
しかし、ローカル環境では必要ない...。
なので、以下のとおり、package.json のビルドコマンドに --base-href オプションを指定することで、ビルド時に「base href」を変え分けられるようにした。

package.json
  :
"scripts": {
    // 起動コマンド
    "dev": "node ./dist/server/server.js",
    // ビルドコマンド (ng build に --base-href オプション追加)
    "build": "npm run build:client && npm run build:server",
    "build:client": "ng build --prod --base-href=/my-app/ && ng run my-app:server",
    "build:server": "tsc --project server --outDir dist --allowjs true",
    :
  },
  :

事象2: バックエンドへの通信時にエラー発生

「事象1」が解消し、画面表示はできるようになったが、
今度は、API でバックエンドにリスト取得する処理を実装したら以下のエラー事象が発生。

ローカルだと取得できてたのに、サーバだとリスト取得できない。。。(本来なら JSON のリストデータが返却される予定)
ブラウザコンソールを開いてみたら、以下のエラーログを吐いていた。

# 発生したエラー

error: SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse...
headers: t {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for https://iexample.com/portal/api/my-app/api/heros"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "https://example.com/my-app/api/heros"

バックエンド通信で、HTTP 結果コードは 200 が返っているが、返却された後に何かエラーが出ている。

error の部分を開いて詳しく見てみると

SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest

さらに、すぐ下に

text: "<!DOCTYPE html><html lang="ja"><head>↵  <meta charset="utf-8">↵  <title>My App</title>↵ 

JSON データ、もしくは、 JSON のリストデータが返却されるべき箇所で、HTML を返しているために、JSON.parse() でエラーが発生したことが原因らしい。

長く悩んだが、よく考えたらそりゃそうだ。Web サービスで画面用のルーティングしか設定してなかった。

▶︎ 解消: Nginx のルーティングに API 専用のルートを追加したら解消

/etc/nginx/conf.d/default.conf
server {
    # http を https にリダイレクト
    listen 80;
    return 301 https://$host$request_uri;
}

server {
    listen       443 ssl;
    server_name  localhost;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # *** 追加: 以下の API 用ルーティングを追加 ↓↓↓ ******
    location /my-app/api/ {
        proxy_pass http://127.0.0.1:3000/api/;
        proxy_redirect default;
    }
    # *** 追加 ↑↑↑ *********************************

    location /my-app/ {
        proxy_pass http://127.0.0.1:3000/;
        proxy_redirect default;
    }

    location / {
        root   /var/www;
        index  index.php index.html index.htm;
    }

    # 404, 50x 等 エラー系のルートが以下にある
}

設定後、nginx -s reload で Nginx を再起動。
Angualar も再デプロイして起動し直し、ブラウザから再度アクセスしてみたら、
エラーは出なくなり、問題なく目的の JSON リストデータが返却されて、画面に表示された。

ハマりどころ

そもそも「Angular の設定方法やロジックに原因がある」として、ずーーーーっと、アプリ設定のネット記事を漁っていたが、そこが間違いだった。

原因の切り分けフロー

HTTP 結果は 200 で、エラーの吐き方が、JSON パースエラー: クラサバ通信はできている
→ リクエストとレスポンスの機構を改めて確認: SSR で実装しているので、リクエスト/レスポンスは HTML を返却するのと JSON データを返却する2種類がある
→ ローカル環境で、ブラウザに GET メソッドの API を叩いてみる: JSON データが返り、画面に表示されることを確認
→ 公開サーバで、同じくブラウザに GET メソッドの API を叩いてみる: トップページが再表示された = HTML が返却された
→ Web サーバのリクエスト URI に対するルーティング先を修正すべきことに気づく
→ Nginx の config を修正して動作確認
問題解消 !!!

こんな感じだった。

エラーの切り分けを丁寧に行い、対応していくことは大事だ。
以上、Web サービスの設定は、大した内容ではないが間違えると結構ハマる。

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7