いつも拝見させていただいております。
ありがとうございます。
現在下記のエラーが発生し、いろいろと試行錯誤しているのですが解決できず、せめて何か解決の糸口となるようなヒントを頂けないかと思い、投稿させていただきました。
現環境
Nuxt.jsとRuby on Railsでシングルページアプリケーションを作り、aws(ECS,EC2)へデプロイしました。
●クラスターの中にNuxt.jsのコンテナ(EC2)とRails(api)のコンテナ(EC2)を配置
●フロントエンド用、バックエンド用のアプリケーションロードバランサーをそれぞれ設置
●ドメインを取得し、Route53のAレコードに登録、証明書発行済み
です。
該当する機能の状況
●フロントエンド側
Nuxt側からaxiosを用いてフォームに入力されたキーワードをパラメーターに含ませ、Railsにgetリクエストを送る
●バックエンド側
Rails側はGoogle Books Apiに対してキーワード検索等を行えるモジュールがあり
このモジュールを専用のmodelにincludeし、model内で定義したメソッドをコントローラで使用して書籍データの入ったJsonをフロントエンドに返却
ローカル環境では機能が正常に作動している事を目視で確認し、また、バックエンド側のテストは終了している状態です。
なお、本番環境においてもGoogle Books Apiを用いない他の機能は正常に作動している事を確認しました。
発生している問題・エラーメッセージ(8月31日修正)
エラーメッセージ(chromeブラウザ、devツールのconsoleから)
「GET https://backend.com/api/v1/books/search/?eyword=%E3%83%8F%E3%83%AA%E3%83%BC 500」の右にある「76cebd9.js:2」について(ソースタブの画像)
/log/production.log(クラスター内のRailsコンテナに入り確認したlog)
ソースコード(フロントエンド側※8月31日訂正)
pages/books.vue
1 2※字数制限によりRailsへのリクエスト部分のみ 3 4<script> 5 6export default { 7 8 methods: { 9 async setBooks () { 10 try { 11 this.books = [] 12 await this.$axios.get(`/api/v1/books/search/?keyword=${this.keyword}`) 13 .then( 14 (response) => { 15 console.log('success--------------') 16 console.log(response) 17 console.log('success--------------') 18 this.books = response.data 19 } 20 ) 21 } catch (error) { 22 console.log('error--------------') 23 console.error(error.response.data); 24 console.log('error--------------') 25 //console.log({error}) 26 //this.error = 'データの取得に失敗しました' 27 this.formReset() 28 } 29 }, 30 formReset () { 31 this.$refs.form.reset() 32 this.keyword = "" 33 }, 34 35</script>
nuxt.config.js
1 2 modules: [ 3 // https://go.nuxtjs.dev/axios 4 '@nuxtjs/axios', 5 '@nuxtjs/auth', 6 'nuxt-i18n', 7 'bootstrap-vue/nuxt', 8 ], 9 10 // Axios module configuration: https://go.nuxtjs.dev/config-axios 11 axios: { 12 baseURL: 'https://backend.com', 13 },
plugins/axios.js
1 2export default function ({ $axios }) { 3 $axios.onRequest((config) => { 4 // Devise Token Auth 5 config.headers.common.uid = localStorage.getItem('uid') 6 config.headers.common.client = localStorage.getItem('client') 7 config.headers.common['access-token'] = localStorage.getItem('access-token') 8 } 9 ) 10 11 $axios.onResponse((response) => { 12 // Devise Token Auth 13 if (response.headers['access-token']) { 14 localStorage.setItem('token-type', response.headers['token-type']) 15 localStorage.setItem('uid', response.headers.uid) 16 localStorage.setItem('client', response.headers.client) 17 localStorage.setItem('access-token', response.headers['access-token']) 18 } 19 } 20 ) 21}
ソースコード(バックエンド側)
lib/google_books_api.rb
1module GoogleBooksApi 2 def url_of_creating_from_id(google_books_api_id) 3 "https://www.googleapis.com/books/v1/volumes/#{google_books_api_id}" 4 end 5 # Google Books APIのIDから、APIのURLを取得する 6 7 def url_of_searching_from_keyword(keyword) 8 "https://www.googleapis.com/books/v1/volumes?q=#{keyword}&country=JP" 9 end 10 # キーワードから、検索するAPIのURLを取得する 11 12 def get_json_from_url(url) 13 JSON.parse(Net::HTTP.get(URI.parse(Addressable::URI.encode(url)))) 14 end 15 # URLから、JSON文字列を取得し、JSONオブジェクトを構築する 16end
models/google_book.rb
1 2require 'google_books_api' 3 4class GoogleBook 5 include ActiveModel::Model 6 include ActiveModel::Attributes 7 include ActiveModel::Validations 8 9 attribute :google_books_api_id, :string 10 attribute :authors 11 attribute :image, :string 12 attribute :published_at, :date 13 attribute :title, :string 14 attribute :publisher, :string 15 16 17 validates :google_books_api_id, presence: true 18 validates :title, presence: true 19 20 class << self 21 #lib以下に作成したモジュールを使用する 22 include GoogleBooksApi 23 24 def new_from_item(item) 25 @item = item 26 @volume_info = @item['volumeInfo'] 27 new( 28 google_books_api_id: @item['id'], 29 authors: @volume_info['authors'], 30 image: image_url, 31 published_at: @volume_info['publishedDate'], 32 title: @volume_info['title'], 33 publisher: @volume_info['publisher'], 34 ) 35 end 36 37 def new_from_id(google_books_api_id) 38 url = url_of_creating_from_id(google_books_api_id) 39 item = get_json_from_url(url) 40 new_from_item(item) 41 end 42 43 def search(keyword) 44 url = url_of_searching_from_keyword(keyword) 45 json = get_json_from_url(url) 46 items = json['items'] 47 return [] unless items 48 49 items.map do |item| 50 GoogleBook.new_from_item(item) 51 end 52 end 53 54 private 55 56 def image_url 57 @volume_info['imageLinks']['smallThumbnail'] if @volume_info['imageLinks'].present? 58 end 59 60 end 61 62 def save 63 return false unless valid? 64 65 book = build_book 66 return false unless book.valid? 67 68 ActiveRecord::Base.transaction do 69 book.save 70 if authors.present? 71 authors.each.with_index do |author, index| 72 author = book.authors.build(name: author) 73 author.is_representative = index.zero? 74 author.save 75 end 76 end 77 end 78 true 79 end 80 81 def find_book_or_save 82 if Book.find_by(google_books_api_id: google_books_api_id) || save 83 Book.find_by(google_books_api_id: google_books_api_id) 84 else 85 false 86 end 87 end 88 89 private 90 91 def build_book 92 Book.new( 93 google_books_api_id: google_books_api_id, 94 published_at: published_at, 95 title: title, 96 image: image, 97 publisher: publisher, 98 ) 99 end 100end
controllers/api/v1/books_controller.rb
1module Api 2 module V1 3 class BooksController < ApplicationController 4 def search 5 books = GoogleBook.search(params[:keyword]) 6 books_array = books.map do |book| 7 { 8 google_books_api_id: book.google_books_api_id, 9 image: book.image, 10 published_at: book.published_at, 11 title: book.title, 12 publisher: book.publisher, 13 authors: book.authors 14 } 15 end 16 books_array.each do |i| 17 if i[:authors] != nil 18 i[:authors] = i[:authors].sort.join(',') 19 end 20 end 21 render json: books_array, status: 200 22 end 23 24 def create 25 google_book = GoogleBook.new_from_id(create_book_params[:google_books_api_id]) 26 if (@book = google_book.find_book_or_save) 27 render json: google_book, status: 200 28 else 29 render json: google_book.errors, status: 500 30 end 31 end 32 33 private 34 35 def create_book_params 36 params.permit(:google_books_api_id) 37 end 38 end 39 end 40end
試したこと
●CORSの設定
当初、CORSエラー(ステータス504)が返ってきていたため
以下の様にRailsのcors.rbを修正しましたが解決しませんでした
config/initializers/cors.rb
1# Be sure to restart your server when you modify this file. 2 3# Avoid CORS issues when API is called from the frontend app. 4# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 6# Read more: https://github.com/cyu/rack-cors 7 8Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 allow do 10 11 origins ['https://backend.com', 'http://localhost:8080'] 12 13 resource "*", 14 headers: :any, 15 expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'], 16 methods: [:get, :post, :put, :patch, :delete, :options, :head] 17 end 18 end
●Chromeブラウザのdevツールによる確認
ググったところ
CORSエラーはRails以外の要因でも発生する事があると知り、devツールのNetworkタブから以下のurlをコピペしブラウザから直接アクセスしたところ、504タイムアウトエラーが発生している事が分かりました
●awsマネジメントコンソール等の確認
参考にした記事
https://qiita.com/nagaakihoshi/items/f7326978221ee053d208
CloudWatch メトリクスのログを確認したところ、クライアント TLS ネゴシエーションエラーが発生している事に気づいたため、公式ページに従ってバックエンド側のロードバランサーの「基本的な設定」タブから「ロードバランサー属性の編集」を行い
〇タイムアウトを60秒から360秒に延長
〇Desyncをモニタリングモードに変更
しました。
その結果CORSエラーは発生しなくなりましたが、表題の通り500エラーが返ってくるようになりました。
補足情報(FW/ツールのバージョンなど)
フロントエンド
"vue": "^2.6.14"
"nuxt": "^2.15.8"
"@nuxtjs/axios": "^5.13.6"
バックエンド
ruby 3.0.2
Rails 6.1.6
開発環境
windows(wsl2 Ubuntu 20.04.5 LTS)
エディタ
vscode
クラスター
EC2(t3.small)
回答1件
あなたの回答
tips
プレビュー