LoginSignup
1
0

More than 1 year has passed since last update.

フロントエンド: react axios バックエンド:SpringBoot JWT認可方式のCRUD処理 検証

Last updated at Posted at 2023-01-08

概要

フロントエンド:react nextjs axios
バックエンド:SpringBoot restapi
でB2CのECサイト作ってます。

SpringBoot側で作ったRestApiをフロントエンド側で動かすことができるか検証します。
ドメインは買い物かごです。

開発環境

開発環境
OS:windows10

バックエンド側:
IDE:IntelliJ Community
spring-boot-starter-parent 2.75
java : 11

データベース
mysql:8.0.29
クライアントソフト: MySQL Workbench

フロントエンド:
IDE:VScode

├── @types/node@18.11.15
├── @types/react-dom@18.0.9
├── @types/react@18.0.26
├── axios@1.2.1
├── eslint-config-next@13.0.6
├── eslint@8.29.0
├── next-auth@4.18.7
├── next@13.0.6
├── react-bootstrap@2.7.0
├── react-dom@18.2.0
├── react@18.2.0
├── styled-components@5.3.6
├── styled-jsx@5.1.1
└── typescript@4.9.4

ユースケース

ユースケースは買い物カゴです。
具体的には
1.顧客の買い物かごの情報を取得する(Read)
2.顧客の買い物かごに商品を追加する (Create)
3.顧客の買い物かごに入ってる商品の数量を変更する(Update)
4.顧客の買い物かごに入っている商品を一つ取り除く(Delete)
5.顧客の買い物カゴに入っている商品を全て乗り除く(Delete)

顧客の認可はSpringBoot側から発行されたjwt方式のトークンを使っています。

ソースコード

Service,repository,Entity分野はここでは書きません。
詳細はgithubのリポジトリで確認お願いします。

バックエンド側

CartItemRestCotnroller.java
package com.example.restapi.implement.cartItem;


import com.example.restapi.domain.cartItem.CartItemService;
import com.example.restapi.domain.customer.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;


@RestController
//@CrossOrigin(origins = "http://127.0.0.1:3000")
@CrossOrigin(origins = "http://localhost:3000")
public class CartItemRestController {
    @Autowired
    CartItemService cartItemService;
    @Autowired
    CustomerService customerService;

    @PostMapping("/api/cart/add")
    public String addProductCart(  @RequestParam("productId") Integer productId,
                                   @RequestParam("quantity") Integer quantity,
                                   HttpServletRequest request
    ){
        String message;
        try {
            //Get customerId from Jwt
            Integer customerId = customerService.getIdfromJwtToken(request);
            cartItemService.addProduct(productId, quantity, customerId);
            // String  email = jwtTokenProvider.getCustomernameFromJWT(token);
            message = "customerId:"+ customerId +  "added" + quantity +"quantity productId" + productId;
            return message;
        }catch(Exception e){
            message = "Exception error";
            return message;
        }
    }

    @GetMapping("/api/cart/all")
    public List<CartItemDto> listCartItems(HttpServletRequest request){
        List<CartItemDto> cartItemDtoList = new ArrayList<>();
        //Get customerId from Jwt
        Integer customerId = customerService.getIdfromJwtToken(request);
        return cartItemService.listCartItems(customerId);
        }

    @PutMapping("/api/cart/update")
    public String updateQuantity(@RequestParam("productId") Integer productId,
                                 @RequestParam("quantity") Integer quantity,
                                 HttpServletRequest request){
        //Get customerId from Jwt
        Integer customerId = customerService.getIdfromJwtToken(request);
        cartItemService.updateQuantity(productId,quantity,customerId);
        return "updated";
    }


    @DeleteMapping("/api/cart/remove")
    public String removeProduct( @RequestParam("productId") Integer productId,
                                 HttpServletRequest request){
            //Get customerId from Jwt
            Integer customerId = customerService.getIdfromJwtToken(request);
            cartItemService.removeProduct(productId,customerId);
            return "ShoppingCart is removed in customerId" + customerId ;
        }

    @DeleteMapping("/api/cart/delete")
    public String deleteByCustomer(HttpServletRequest request){
        //Get customerId from Jwt
        Integer customerId = customerService.getIdfromJwtToken(request);
        cartItemService.deleteByCustomer(customerId);
         return customerId + "is all deleted in all ShoppinCart";
    }





}



フロントエンド側

carItemServiceCheck.tsx
import { useState, useEffect, useCallback } from "react";
import { CartItem } from "../../types/cartItem";
import { Customer } from "../../types/customer";
import * as customerService from "../../service/customerService";
import * as cartItemService from "../../service/cartItemService";

/**
 * 
 * @remarks SpringBootのRestAPIを nextjs側が動かせるかの検証用ページ
 * 
 * @authoer RYA234
 * 
 * @returns 
 */
export default function CartItemServiceCheck(this: any) {
  const [currentCartItems, setCurrentCartItems] = useState<CartItem[]>([]);
  const [jwtAccessToken, setJwtAccessToken] = useState<string | null>();

  // ログイン処理は初回時のみ実行 CartItemドメインの検証用ページなので
  useEffect(() => {
    loginAndGetCartItem(), [];
  });

  // ログイン処理後カートアイテムの情報取得更新する
  const loginAndGetCartItem = async () => {
    await login();
    console.log("await between login and getCartItems");
    await getCartItems();
  };

  // ログインする用のAPIを叩く
  const login = () => {
    const requestBody: Customer = {
      email: "aaaba@gmail.com",
      password: "test",
      append: function (arg0: string, inputEmail: string): unknown {
        throw new Error("Function not implemented.");
      },
    };
    customerService
      .signIn(requestBody)
      .then((response) => {
        localStorage.setItem("accessToken", response.data.accessToken);
        setJwtAccessToken(response.data.accessToken);
        console.log("accesssToken is set");
      })
      .catch((error) => {
        localStorage.setItem("accessToken", " error");
        setJwtAccessToken(localStorage.getItem("accessToken"));
        console.log("accesssToken is not set");
      });
  };

  // カートアイテムの情報を取得する
  const getCartItems = () => {
    cartItemService
      .getCartItems(jwtAccessToken as string)
      .then((response) => {
        // 再レンダリングを防ぐために、レスポンスのデータが変わっている場合のみsetCurrentCartItemsを実行する。
        // if ((response.data as CartItem[]).toString() != currentCartItems.toString()) // ToString()だとエラーになるのでJSON.stringifyに修正
        if (
          JSON.stringify(response.data as CartItem[]) !=
          JSON.stringify(currentCartItems)
        ) {
          setCurrentCartItems(response.data);
          console.log("cartItems are set");
          console.log(response.data);
        }
      })
      .catch((error) => {
        console.log("cartItems are not set");
      });
  };

  let inputProductId: number = 0;
  let inputQuantity: number = 0;

  // カートアイテムを追加する
  const addCartItem = async () => {
    const inputCartItem: CartItem = {
      productId: inputProductId as number,
      quantity: inputQuantity as number,
      id: 0,
      customerId: 0,
    };
    await cartItemService
      .addProductCart(inputCartItem, jwtAccessToken as string)
      .then((response) => {
        console.log(response.data);
        console.log("cartItems is added");
      })
      .catch((error) => {
        console.log("cartItems are not set");
      });
  };

  // データベースに追加後、データベースから再取得する
  // ボタンに割り当てる
  const addCartItemHandler = async () => {
    console.log("jwt" + jwtAccessToken);
    await addCartItem();
    console.log("between addCartItem and getCartItems");
    await getCartItems();
  };

  // カートアイテムの数量を更新後、データベースから再取得して画面更新する。
  // ボタンに割り当てる
  const updateQuantitAndGetCartItemsyHandler = async () => {
    await updateQuantity();
    console.log("between updateQuantity and getCartItems");
    await getCartItems();
  };

  let updateInputquantiy: number = 0;
  let updateInputProductId: number = 0;
  // カートアイテムの数量を更新する
  const updateQuantity = async () => {
    const updateCarItem: CartItem = {
      productId: updateInputProductId as number,
      quantity: updateInputquantiy as number,
      id: 0, // 使わない
      customerId: 0, // 使わない
    };
    await cartItemService
      .updateQuantity(updateCarItem, jwtAccessToken as string)
      .then((response) => {
        console.log(response.data);
      })
      .catch((error) => {
        console.log("update is failed");
      });
  };
  let removeInputProductId: number = 0;

  // removeInputProducIdの値の商品を削除後 再度データベースから取得して画面更新する。
  // ボタンに割り当てる
  const removeCartItemAndGetItemsHandler = async () => {
    await removeCartItem();
    await getCartItems();
  };

  // removeInputProducIdの値の商品を削除する
  const removeCartItem = async () => {
    const removeCartItem: CartItem = {
      productId: removeInputProductId as number,
      id: 0,
      customerId: 0,
      quantity: 0,
    };

    await cartItemService
      .removeProduct(removeCartItem, jwtAccessToken as string)
      .then((response) => {
        console.log(response.data);
      })
      .catch((error) => {
        console.log(error);
      });
  };
  // 顧客のカートアイテム全削除後、再度データベースから取得して画面更新する。
  // ボタンに割り当てる
  const deleteAndGetItemsHandler = async () => {
    await deleteCartItem();
    await getCartItems();
  };

  // 顧客のカートアイテム全削除する
  const deleteCartItem = async () => {
    await cartItemService
      .deleteByCustomer(jwtAccessToken as string)
      .then((response) => {
        console.log(response.data);
      })
      .catch((error) => {
        console.log(error);
      });
  };

  return (
    <>
      <h1>カートアイテムの取得追加更新削除 検証ページ</h1>
      <div>aaabaa@gmail.comでログインしているものとする</div>
      <h1>カートアイテムの全取得</h1>
      <div>リアルタイムで表示されます</div>
      顧客ID,商品ID,数量
      {currentCartItems.map((cartItem: CartItem) => {
        return (
          // eslint-disable-next-line react/jsx-key
          <div>
            <div>
              {cartItem.customerId},{cartItem.productId},{cartItem.quantity}
            </div>
          </div>
        );
      })}
      <br />
      <h1>カートアイテム追加</h1>
      <label>商品ID</label>
      <input
        type="text"
        placeholder="商品ID"
        onChange={(event) =>
          (inputProductId = event.target.value as unknown as number)
        }
      />
      {/* onchangeをするとloginAndGetCartItemが実行されるのが良くない */}
      <br />
      <label>個数</label>
      <input
        type="text"
        placeholder="1"
        onChange={(event) =>
          (inputQuantity = event.target.value as unknown as number)
        }
      />
      <button onClick={addCartItemHandler}>カートアイテム追加</button>
      <h1>カートアイテムの数量を変更</h1>
      <label>数量変更する商品ID</label>
      <input
        type="text"
        placeholder="商品ID"
        onChange={(event) =>
          (updateInputProductId = event.target.value as unknown as number)
        }
      />
      <br />
      <label>数量</label>
      <input
        type="text"
        placeholder="数量"
        onChange={(event) =>
          (updateInputquantiy = event.target.value as unknown as number)
        }
      />
      <br />
      <button onClick={updateQuantitAndGetCartItemsyHandler}>数量変更</button>
      <h1>カートアイテムを1つ削除</h1>
      <label> 商品ID</label>
      <input
        type="text"
        placeholder="削除したい商品ID"
        onChange={(event) =>
          (removeInputProductId = event.target.value as unknown as number)
        }
      />
      <br />
      <button onClick={removeCartItemAndGetItemsHandler}>remove</button>
      <h1>カートアイテムを全削除</h1>
      <button onClick={deleteAndGetItemsHandler}>
        delete カートアイテム全削除
      </button>
    </>
  );
}



cartItemService.ts
import axios from 'axios';
import { CartItem } from '../types/cartItem';

/**
 * 
 * @remarks SpringBootで作ったAPIを呼ぶ関数
 * 
 * 
 * @param jwtAccessKey 
 * @returns 
 */

export const addProductCart = ({productId,quantity}: CartItem, jwtAccessKey:string) =>{
	return axios.post(`http://127.0.0.1:5000/api/cart/add`
	+ `?productId=` +productId
	+ `&quantity=` +quantity
	,{},
	{ headers : {
		'Authorization': `Bearer ${jwtAccessKey}`,
		'Request-Method' : 'POST',
		'Content-Type': 'application/json',
		'Access-Control-Allow-Origin': 'http://127.0.0.1:5000/*',
		'Access-Control-Allow-Headers': 'accept, accept-language, content-language, content-type',
		'Access-Control-Allow-Credentials': 'true'
	}}
	);
}


//
export const getCartItems =(jwtAccessKey : string)=>{
	return axios.get(`http://127.0.0.1:5000/api/cart/all`,
	{ headers : {
			'Authorization': `Bearer ${jwtAccessKey}`
		}}
	);
}

// 
export const updateQuantity = ({productId,quantity}:CartItem,jwtAccessKey:string) =>{
	return axios.put(`http://127.0.0.1:5000/api/cart/update`
	+ `?productId=` +productId
	+ `&quantity=` +quantity,
	{},
	{ headers : {
			'Authorization': `Bearer ${jwtAccessKey}`,
			'Request-Method' : 'PUT',
			'Content-Type': 'application/json',
			'Access-Control-Allow-Origin': 'http://127.0.0.1:5000/*',
			'Access-Control-Allow-Headers': 'accept, accept-language, content-language, content-type',
			'Access-Control-Allow-Credentials': 'true'
	}}
	)
}

export const removeProduct = ({productId}:CartItem, jwtAccessKey:string) =>{
return axios.delete(`http://127.0.0.1:5000/api/cart/remove`
	+`?productId=`+ productId,
	{ headers : {
			'Authorization': `Bearer ${jwtAccessKey}`
	}}
);
}

export const deleteByCustomer = (jwtAccessKey:string) =>{
	return axios.delete(`http://127.0.0.1:5000/api/cart/delete`,
	{ headers : {
			'Authorization': `Bearer ${jwtAccessKey}`
	}}
);
}

cartItem.d.ts


export type CartItem = {
	id:number;	// 買い物かごのID
	customerId:number // 顧客のID
	productId:number // 商品のId
	quantity: number  // 数量 

}

動きを確認

gifアニメで実際の動きを記録します。
画面左側はブラウザ 右側はmysql workbenchです。

追加時の処理

商品ID23 を2個追加している様子です。

cartItem_add.gif

github

数量変更時

商品ID23 を5個に変更している様子です。

cartItem_update.gif

github

買い物かごから選択した商品IDを削除

商品ID4を買い物カゴを削除している様子です。
cartItem_remove.gif

github

買い物カゴ全削除

買い物カゴの中身を全削除している様子です。
cartItem_delete.gif

github

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