フリーランス 技術調査ブログ

フリーランス/エンジニア Ruby Python Nodejs Vuejs React Dockerなどの調査技術調査の備忘録

YahooAPIでエラーになったので解消(React+Express)

はじめに

  • 久しぶりにバーコード読み込み機能を動作させたらエラーが何件が発生した px-wing.hatenablog.com

  • 1件目は下記のエラー

index.js:1 Warning: validateDOMNesting(...): <tr> cannot appear as a child of <table>. Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated by the browser.
    in tr (at BarcodeScan.js:90)
  • 2件目はYahooAPIの商品検索が実行できない。

原因と解決方法

  • 1件目の対応はReactでtableタグを出力する際はtbodyタグが必要ということでtbodyタグを追加することで解消できた。 github.com

  • 2件目のForbiddenエラーが発生した 原因を調べるとアクセス制限数が上限を超えているというエラーだが、利用数の上限を超えるほど利用していないため、調査したところ、YahooAPIの検索の仕様が変わっていたい

修正前

const result = await axios.get(`https://shopping.yahooapis.jp/ShoppingWebService/V1/json/itemSearch?appid=<あなたのAPIKey>&jan=${req.query.barcode}  .then(res =>{
    return res.data.ResultSet[0].Result[0]
})

修正後

const result = await axios.get(`https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?appid=<あなたのAPIKey>&jan_code=${req.query.barcode}.then(res =>{
  return res.data.hits[0]
})

YahooAPIの変更点

  • エンドポイントのURLの変更
  • パラメータの変更
  • レスポンスで出力されるJsonの形式

実際に変更したプログラム

Express側

router.get('/product_search', async (req, res, next) =>{
  logger.app.debug(req.query.barcode)
  const result = await axios.get(`https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?appid=<あなたのAPIキー>&jan_code=${req.query.barcode}`)
  .then(res =>{
    return res.data.hits[0]
  })
  res.json(result)
})

React

import React , { useState,useEffect } from 'react'
import Quagga from "quagga";
import axios from 'axios'

const BarcodeScan = (props) => {

  const [barcode,setBarcode] = useState("")
  const [product,setProduct] = useState({})

  const barcodeApi = async (barcode) => {
    
    const result = await axios.get(`https://<express側のURLを指定する>?barcode=${barcode}`)
    .then(res =>{
      const endAnnounce = new SpeechSynthesisUtterance("読み込みました")           
      speechSynthesis.speak(endAnnounce)
      console.log(res.data)
      return setProduct(res.data)
    })
    console.log(result)
  }
  

  const config = {
    inputStream: {
      name : "Live",
      type : "LiveStream",        
      target: '#preview',
      size: 1000,
      singleChannel: false
    },
    locator: {
      patchSize: "medium",
      halfSample: true
    },
    decoder: {
      readers: [{
        format: "ean_reader",
        config: {}
      }]
    },
    numOfWorker: navigator.hardwareConcurrency || 4,
    locate: true,
    src: null
  };

  useEffect(() => {
    Quagga.onDetected(result => {
      if (result !== undefined){
        setBarcode(result.codeResult.code)
      }
    });

    
    Quagga.init(config, function(err) {
        if (err) {
            console.log(err);
            return
        }
        Quagga.start();
    });
  
  },[])

  useEffect(() => {
    console.log(barcode)
    if (barcode){
      Quagga.stop()
      barcodeApi(barcode)
      Quagga.init(config, function(err) {
        if (err) {
            console.log(err);
            return
        }
        Quagga.start();
    });

      }
  },[barcode])


  return (
    <>
      <h2>バーコードスキャナ</h2>
      <hr />
      {barcode !== "" ? `バーコード:${barcode}` : "スキャン中"}
      <hr />
      {
        Object.keys(product).length ?
          <table>
            <tbody>
              <tr>
                <td>商品名</td>
                <td><a href={product.url}>{product.name}</a></td>
              </tr>
              <tr>
                <td>商品詳細</td>
                <td>{product.description}</td>
              </tr>
              <tr>
                <td>商品画像</td>
                <td>{product.image ? <img src={product.image.small} /> : '画像はありません'}</td>
              </tr>
              </tbody>
            </table>             
          : ''
      }
      <div id="preview"></div>
    </>
  )
}

export default BarcodeScan