0
0

クライアントからSpring WebFluxにAPIアクセスを試してみた

Posted at

背景・目的

過去に、Spring Bootについて下記の記事にまとめました。今まではサーバサイドとは代わり、クライアント側からサーバサイドアプリケーションを呼び出す処理を書いてみます

まとめ

下記に特徴をまとめる

特徴 説明
React リアクティブなをフレームワーク

Reactは、Metaが開発するOSSのフロントエンドフレームワークである
CORS ・Cross-Origin Resource Sharingの略

・HTTPヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権をブラウザに指示する仕組み

・セキュリティ上の理由から、ブラウザにはスクリプトにより開始されるオリジン間HTTPリクエストを制限している

概要

React

Webページからリアルタイムにバックエンドへアクセスし、常に表示が更新されるアプリケーションの場合、リアクティブな処理をフレームワークに実装する事が多い。その一つとしてReactがある。

  • Reactは、Metaが開発するOSSのフロントエンドフレームワークである
  • Reactの開発を行うには、Node.jsが必要

Reactプロジェクトの基本構成

構成 種別 説明
node_modules フォルダ アプリで利用するパッケージが保管されている
public フォルダ 公開フォルダ
トップページであるindex.htmlやログのファイルが用意されている
src フォルダ アプリケーションの本体
Reactアプリのコードを用意する
.gitignore ファイル Gitが利用するファイル
Gitのトラッキングの対象外とするファイルやディレクトリを指定できる。
package.json プロジェクトのパッケージ情報が記載されている
package-lock.json パッケージ管理ツールnpmが使う
README.md 使用方法等が記載されている

CORS

オリジン間リソース共有 (Cross-Origin Resource Sharing, CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。ウェブアプリケーションは、自分とは異なるオリジン (ドメイン、プロトコル、ポート番号) にあるリソースをリクエストするとき、オリジン間 HTTP リクエストを実行します。

オリジン間リクエストとは、例えば https://domain-a.com で提供されているウェブアプリケーションのフロントエンド JavaScript コードが XMLHttpRequest を使用して https://domain-b.com/data.json へリクエストを行うような場合です。

セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、 XMLHttpRequestや Fetch API は同一オリジンポリシー (same-origin policy) に従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンからの場合は正しい CORS ヘッダーを含んでいることが必要です。

  • Cross-Origin Resource Sharingの略
  • HTTPヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権をブラウザに指示する仕組み
  • セキュリティ上の理由から、ブラウザにはスクリプトにより開始されるオリジン間HTTPリクエストを制限している

実践

前提

前回、構築したSpringBootでSpring WebFluxを試してみたを使用します。

静的HTMLファイル

まずは、静的なアクセスから試します

  1. resourcesフォルダの配下に、publicフォルダを作成します
    image.png

  2. static.htmlファイルを作成します
    image.png

  3. static.htmlに下記のコードを書きます

    <!DOCTYPE html>
    <html>
      <head>
        <title></title>
        <meta http-equiv="Content-type" content="text/html"; charset="UTF-8" />
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" >
      </head>
      <body class="container">
        <h1 class="display-4">Static html</h1>
        <p class="msg">This is static html file.</p>
      </body>
    </html>
    

確認

  1. ビルドとSpring Bootを起動します
    $ mvn package
    $ java -jar target/sample-0.0.1-SNAPSHOT.jar
    
  2. ブラウザでlocalhost:8080/static.html にアクセスします。表示されました
    image.png

fetch関数

JavaScriptを使用してAPIにアクセスします。JavaScriptで指定したURLにアクセスするには、fetch関数を使います。
この、fetch関数は、非同期になるため、thenコールバック処理を用意するか、awaitでも戻り値を受け取り処理します。

APIからPostレコードを取得する

  1. static.htmlを下記のように修正します
    <!DOCTYPE html>
    <html>
      <head>
        <title></title>
        <meta http-equiv="Content-type" content="text/html"; charset="UTF-8" />
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" >
      </head>
      <body class="container">
        <h1 class="display-4">Static html</h1>
        <p class="msg">This is static html file.</p>
        <div class="border border border-1 p-3" id="container"></div>
        <script>
          async function getData() {
            const response = await fetch('/post');
            const data = await response.json();
            document.querySelector('#container').textContent = JSON.stringify(data);
          };
          getData();
        </script>
      </body>
    </html>
    
    • /postにアクセスし、ダミーとして作成したPostを取得している

確認

  1. ビルドとSpring Bootを起動します
    $ mvn package
    $ java -jar target/sample-0.0.1-SNAPSHOT.jar
    
  2. ブラウザでlocalhost:8080/static.html にアクセスします。表示されました
    image.png

## フォーム入力を利用する
ユーザからの入力に応じて、アクセスする場合を想定します。

HTMLを修正する

  1. 下記のように修正します
    <!DOCTYPE html>
    <html>
      <head>
        <title></title>
        <meta http-equiv="Content-type" content="text/html"; charset="UTF-8" />
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" >
      </head>
      <body class="container">
        <h1 class="display-4">Static html</h1>
        <p class="msg">This is static html file.</p>
        <div class="border border-1 p-3 mb-4" id="area">no data.</div>
        <div class="input-group">
          <input type="text" class="form-control me-1" 
            id="input" />
          <span class="input-group-btn">
            <input type="submit" class="btn btn-primary px-4" 
              onclick="submit();" value="Click" /> 
          </span>
        </div>
        <script>
          async function submit() {
            const id = document.querySelector("#input").value;
            const result = await getData('/post/' + id);
            document.querySelector('#area').textContent 
              = JSON.stringify(result);
          }
          async function getData(url) {
            const response = await fetch(url);
            return await response.json();
          };
          getData();
        </script>
      </body>
    </html>
    

確認

  1. ビルドとSpring Bootを起動します

    $ mvn package
    $ java -jar target/sample-0.0.1-SNAPSHOT.jar
    
  2. ブラウザでlocalhost:8080/static.html にアクセスします。表示されました
    image.png

  3. 次に1を入力し、「Click」ボタンをクリックします。no dataから値が変わりました
    image.png

Reactアプリケーション

環境構築

Reactの開発を行うために、Node.jsをインストールします

  1. brewでインストールします。その他の方法は、https://nodejs.org/en/download/package-managerを参考にインストールしてください
    $ brew install node@20
    
  2. パスを通します
    $ echo 'export PATH="/usr/local/opt/node@20/bin:$PATH"' >> /Users/XXXX/.zshrc
    $ source /Users/XXXX/.zshrc 
    $ node -v
    v20.15.1
    $ npm -v
    10.7.0
    $
    

Reactプロジェクトを作成

  1. プロジェクト用のフォルダを作成する
    $ mkdir react
    $ cd react 
    $
    
  2. プロジェクトを作成する
    npx create-react-app react_flux_app
    
  3. できました
    $ ls
    react_flux_app
    $
    
  4. 下記の構成になっています
    image.png

Appコンポーネントからアクセスする

App.js

  1. srcフォルダのApp.jsを開きます
    image.png

  2. 下記のように修正します

    import React, {useState} from 'react';
    import './App.css';
    
    function App() {
      const url = "http://localhost:8080/post";
      const [data,setData] = useState("");
    
      async function getData(){
        const response = await fetch(url);
        const data = await response.json();
        const str = JSON.stringify(data, null, '  ');
        setData(str);
      }
    
      getData();
    
      return(
        <div className='App'>
          <h1> React app.</h1>
          <pre>{data}</pre>
        </div>
      )
    }
    
    export default App;
    

App.css

  1. srcフォルダのApp.cssを開きます
  2. 下記のコードを追記します
    pre{
      border:1px solid gray;
      text-align: left;
      padding: 20px;
      margin: 20px;
    }
    
  3. Nodeを起動します
    $ npm start
    

確認

  1. ブラウザにlocalhost:3000/ を入力しアクセスします
    image.png

CORSの設定

上記のままでは、アクセスができません。
WebFluxアプリと、Reactアプリでは、ポートが異なるため、オリジンが異なると判断されアクセスを拒否されています。
これを回避するために、CORS(Cross-Origin Resource Sharing)の設定を行います。

SampleRestControllerクラスの修正

WebFluxアプリにCrossOriginアノテーションを設定します

  1. postメソッドにアノテーションを追加します
      @RequestMapping("/post")
      @CrossOrigin(value={"http://localhost:3000/"})
      public Mono<Post> post(){
        Post post = new Post(0,0,"title-a","body-a");
        return Mono.just(post);
      }
    

確認

  1. ビルドとSpring Bootを起動します
    $ mvn package
    $ java -jar target/sample-0.0.1-SNAPSHOT.jar
    
  2. ブラウザにlocalhost:3000/ を入力しアクセスします。見えました
    image.png

SWR

Reactでは、ステートフックと呼ばれるものを利用することで、値が更新されると自動的にWebページの表示も更新されるような仕組みを簡単に作成できます。

このステートフックとして、NWアクセスの取得結果を扱えるようにするパッケージが用意されている。SWRです。
利用することで、URLの値を変更するとリアルタイムにデータの内容が更新されるような処理を作成できます。

環境構築

  1. SWRをインストールします

    $ npm install swr
    $ npm list       
    react_flux_app@0.1.0 /Users/XXX/git/react/react_flux_app
    ├── @testing-library/jest-dom@5.17.0
    ├── @testing-library/react@13.4.0
    ├── @testing-library/user-event@13.5.0
    ├── react-dom@18.3.1
    ├── react-scripts@5.0.1
    ├── react@18.3.1
    ├── swr@2.2.5
    └── web-vitals@2.1.4
    
    $
    

コントローラ

  1. SampleRestControllerを修正します。CrossOriginアノテーションを追加します
      @RequestMapping("/post/{id}")
      @CrossOrigin(value={"http://localhost:3000/"})
      public Mono<Post> post(@PathVariable int id){
        Post post = repository.findById(id);
        return Mono.just(post);
      }
    
    

ビルドと起動

  1. ビルドとSpring Bootを起動します
    $ mvn package
    $ java -jar target/sample-0.0.1-SNAPSHOT.jar
    

App.jsを修正

  1. App.jsを下記のように修正します
    import React, {useState} from 'react';
    import useSWR from 'swr';
    import './App.css';
    
    function App() {
      const baseUrl = "http://localhost:8080/post";
      const fetcher = (url) => fetch(url).then(res => res.json());
      const [url,setUrl] = useState(baseUrl);;
      const {data} = useSWR(url, fetcher);
    
      const updateUrl = (e)=> {
        setUrl(baseUrl + '/' + e.target.value);
      }
    
      return(
        <div className="App">
          <h1>React App</h1>
          <input type="number" onChange={updateUrl} />
          <pre>
            <ul>
              <li>ID:     {data ? data.id : ''}</li>
              <li>USERID: {data ? data.userId : ''}</li>
              <li>TITLE:  {data ? data.title : ''}</li>
              <li>BODY:   {data ? data.body : ''}</li>
            </ul>
          </pre>
        </div>
      );
    
    }
    export default App;
    

App.cssを修正

  1. App.cssを下記のように修正します
    ul{
      text-align: left;
      font-size: large;
      font-weight: bold;
    }
    

Nodeを起動

  1. Nodeを起動します
    $ npm start
    

確認

  1. ブラウザに、localhost:3000/を入力します
    image.png

  2. プルダウンで1を選択します。反映されました
    image.png

  3. プルダウンで2を選択します。反映されました
    image.png

  4. プルダウンで3を選択します。反映されました
    image.png

考察

今回は、Reactを使って、Spring WebFluxにAPIアクセスを試してみました。クライアントからのアクセスとサーバサイドのAPIの挙動について、理解ができました。
今後は、Beanについて試してみたいと思います。

参考

Spring Boot 3 プログラミング入門

0
0
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
0
0