

# 環境

rails 6.0.2.1  
ruby 2.6.3  
amazon linux  
mysql  
unicorn  
nginx

# 実現したいこと

現在、インスタクローンアプリを作成し、AWSにデプロイしています。  
「かんたんログイン」と「通常ユーザのログアウト機能」を本番環境でも開発環境と同様に動くようにしたいです。

# 起きているエラー

・本番環境でかんたんログインができない  
・通常ユーザでログアウトできない（ログインは可能）

・追記  
開発環境では問題なく動くためルーティングの設定自体は間違っていないと思われます。  
問題は開発環境ではpost及びdeleteで動いていたものが本番環境ではgetとして動いてしまっている点です。

両者ともエラー発生時には

[code]

    The page you were looking for doesn't exist.
    You may have mistyped the address or the page may have moved.
    
[/code]

のエラー画面、及びlog/production.log上で

[code]

    ActionController::RoutingError
    
[/code]

が出ています。

# 関連するソースコード

routes.rb

[code]

    Rails.application.routes.draw do
      get 'likes/create'
      get 'likes/destroy'
      get 'sessions/new'
      root 'main_page#main'
      get '/signup', to: 'users#new'
      get '/login', to: 'sessions#new'
      post '/login', to: 'sessions#create'
      delete '/logout', to: 'sessions#destroy'
      get '/newpost', to: 'microposts#new'
    
      resources :users do
        member do
          get :following, :followers
        end
        resources :favorites, only: :index
      end
    
      resources :microposts, only: [:create, :destroy, :show] do
        resources :comments, only: [:create, :destroy]
        resource :favorites, only: [:create, :destroy]
      end
    
      resources :relationships, only: [:create, :destroy]
      resources :testsessions, only: :create
      resources :likes, only: [:create, :destroy]
    end
    
[/code]

testsessions_controller.rb(かんたんログイン)

[code]

    class TestsessionsController < ApplicationController
      def create
        user = User.find_by(email:"test@test.com")
        session[:user_id] = user.id
        flash[:success] = "テストユーザとしてログインしました。"
        redirect_to root_url
      end
    end
    
[/code]

sessions_controller.rb

[code]

    class SessionsController < ApplicationController
      def new
      end
    
      def create
        user = User.find_by(email: params[:session][:email].downcase)
        if user && user.authenticate(params[:session][:password])
          log_in user
          redirect_back_or user
        else
          flash.now[:danger] = "メールアドレスとパスワードの組み合わせが違います"
          render 'new'
        end
      end
    
      def destroy
        log_out
        redirect_to root_url
      end
    end
    
[/code]

sessions_helper.rb

[code]

    module SessionsHelper
      def log_in(user)
        session[:user_id] = user.id
      end
    
      def current_user?(user)
        user == current_user
      end
    
      def current_user
        if session[:user_id]
          @current_user ||= User.find_by(id: session[:user_id])
        end
      end
    
      def logged_in?
        !current_user.nil?
      end
    
      def log_out
        session.delete(:user_id)
        @current_user = nil
      end
    
      # 記憶したURL (もしくはデフォルト値) にリダイレクト
      def redirect_back_or(default)
        redirect_to(session[:forwarding_url] || default)
        session.delete(:forwarding_url)
      end
    
      # アクセスしようとしたURLを覚えておく
      def store_location
        session[:forwarding_url] = request.original_url if request.get?
      end
    end
    
[/code]

users_controller.rb

[code]

    class UsersController < ApplicationController
      before_action :logged_in_user, only: [:edit, :update, :destroy, :following, :followers]
      before_action :test_user, only: [:edit, :destroy, :following, :followers]
      before_action :correct_user, only: [:edit, :update]
    
      def show
        @user = User.find(params[:id])
        @microposts = @user.microposts.paginate(page: params[:page])
      end
    
      def new
        @user = User.new
      end
    
      def create
        @user = User.new(user_params)
        if @user.save
          log_in @user
          flash[:success] = "登録が完了しました"
          redirect_to @user
        else
          render 'new'
        end
      end
    
      def edit
        @user = User.find(params[:id])
      end
    
      def update
        @user = User.find(params[:id])
        if @user.update_attributes(user_params)
          flash[:success] = "プロフィールを更新しました"
          redirect_to @user
        else
          render 'edit'
        end
      end
    
      def following
        @title = "フォロー中"
        @user = User.find(params[:id])
        @users = @user.following.paginate(page: params[:page])
        render 'show_follow'
      end
    
      def followers
        @title = "フォロワー"
        @user = User.find(params[:id])
        @users = @user.followers.paginate(page: params[:page])
        render 'show_follow'
      end
    
      private
    
        def user_params
          params.require(:user).permit(:name, :email, :password, :password_confirmation, :usericon)
        end
    
        def correct_user
          @user = User.find(params[:id])
          redirect_to(root_url) unless current_user?(@user)
        end
    
        def test_user
          @user = User.find(1)
        end
    end
    
[/code]

user.rb

[code]

    class User < ApplicationRecord
      has_many :microposts, dependent: :destroy
      has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
      has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
      has_many :following, through: :active_relationships, source: :followed
      has_many :followers, through: :passive_relationships, source: :follower
      has_many :likes, dependent: :destroy
      has_many :comments, dependent: :destroy
      has_many :favorites, dependent: :destroy
      has_many :favorite_microposts, through: :favorites, source: :micropost
      before_save { self.email = email.downcase }
      validates :name, presence: true, length: { maximum: 50 }
      VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
      validates :email, presence: true, length: { maximum: 200 }, format: { with: VALID_EMAIL_REGEX },
                uniqueness: { case_sensitive: false }
      has_secure_password
      validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
      validate :usericon_size
      mount_uploader :usericon, UsericonUploader
    
      def User.digest(string)
        cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
        BCrypt::Password.create(string, cost: cost)
      end
    
      def feed
        Micropost.where("user_id = ?", id)
      end
    
      def feed
        following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id"
        Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)
      end
    
      def follow(other_user)
        following << other_user
      end
    
      def unfollow(other_user)
        active_relationships.find_by(followed_id: other_user.id).destroy
      end
    
      def following?(other_user)
        following.include?(other_user)
      end
    
      private
        def usericon_size
          if usericon.size > 5.megabytes
            errors.add(:usericon, "画像サイズを5MB以下にしてください")
          end
        end
    end
    
[/code]

_header.html.erb

[code]

    <header class="navbar navbar-fixed-top navbar-inverse">
      <div class="container">
        <%= link_to "Portgram", root_path, id: "logo" %>
        <nav>
        <% if logged_in? %>
          <ul class="nav navbar-nav navbar-center">
            <%= form_tag root_path, :method => 'get' do %>
              <%= text_field_tag :search, params[:search], placeholder: "投稿を検索" %>
              <%= submit_tag "検索", :name => nil, class: "btn btn-primary" %>
            <% end %>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><%= link_to "投稿", newpost_path %></li>
            <li><%= link_to "お気に入り", user_favorites_url(current_user) %></li>
            <li><%= link_to "マイページ", current_user %></li>
            <li><%= link_to "ログアウト", logout_path, method: :delete %></li>    
          </ul>
        <% end %>
        </nav>
      </div>
    </header>
    
[/code]

main.html.erb

[code]

    <% if logged_in? %>
      <div class="row">
        <aside class="col-md-4">
          <section class="user_info">
            <%= render 'shared/user_info' %>
          </section>
        </aside>
        <div class="col-md-8">
          <%= render 'shared/feed' %>
        </div>
      </div>
    <% else %> 
      <div class="center jumbotron background">
        <h1>Portgram</h1>
        <p>海での思い出をみんなと共有しよう</p>
    
        <%= link_to "今すぐ始める", signup_path, class:"btn btn-lg btn-primary" %>
        <%= link_to "ログイン", login_path, class:"btn btn-lg btn-primary" %>
        <%= link_to "かんたんログイン", testsessions_path, method: :post, class:"btn btn-lg btn-primary" %>
      </div>
    <% end %>
    
[/code]

# 試したこと

・アセットのコンパイル  
・開発環境での動作確認→全て問題なし

調べたところ、herokuやdeviseで実装した場合の情報はちらほら出ていますが、私は使っていないので参考になりませんでした。  
なかなか同様の事例がないので進展がありません。

分かる方がいらっしゃればアドバイスお願いいたします。

# 追記

googleの検証ツールも確認したところ、以下と非常に近い状況です。  
jsファイルが読み込めていないのが関連していると考えていますが、そうだとすると何故プリコンパイルで解決しないのかがわかりません。

<http://www.366service.com/jp/qa/9ece5f5fd1de61c43a51cd864834174d>


CC BY-SA 4.0
2020-05-29T07:56:14.250
67119
2021-07-04T05:22:55.593
2020-05-29T10:48:43.613
39665
39665
question
javascript ruby-on-rails aws
Railsで作成したアプリが本番環境(AWS)でのみルーティングエラーを出す


自己解決しました。  
直接問題を解決したというよりは、別の方法で問題を回避したという方が近いのですが・・・。

ルーティングでかんたんログイン及びログアウトをそれぞれpost,deleteのHTTPメソッドにしていたところ、  
getに変更することで動くようになりました。  
下記記事等を参考にしたのですが、jsでの読み込みが関係しているようです。

しかし、どのように影響しているかまでは明確にはわかりませんでした。

<https://nisshiee.hatenablog.jp/entry/2017/05/12/100725>  
<https://stackoverflow.com/questions/23368994/no-route-matches-get-logout-
rails>  
<https://sevasu.net/programming-2/>  
[Railsアプリケーションが本番環境にてcssが効かずに困っています。](https://ja.stackoverflow.com/questions/10699/rails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%8C%E6%9C%AC%E7%95%AA%E7%92%B0%E5%A2%83%E3%81%AB%E3%81%A6css%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%9A%E3%81%AB%E5%9B%B0%E3%81%A3%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99)


CC BY-SA 4.0
2020-05-30T00:53:19.007
67132
2020-05-30T00:53:19.007
39665
67119
answer


JavaScriptが正しく動作していないため、POSTやDELETEとして処理されず、ルーティングが失敗しています。
**JavaScriptが正しく動作するように修正してください。**
ただし、その解決にはブラウザの検証ツールに出るエラーメッセージや生成されたHTMLのコード、JavaScriptのファイルに直接アクセスした場合はどうなるかなどが必要になるため、一概に解決方法は示せません。(もし、その問題を解決したい場合は、質問の要旨が変わってきているので、この質問に関連付けながら、別質問にした方がいいと思います。)

GETでも読み込めるようにルーティングを変更することで対応しているようですが、それはあまり良い方法ではありません。ちゃんとJavaScriptが動作するように対応すべきです。

下記に原因を細かく解説します。

* * *

**なぜ、JavaScriptが正しく動作しないとPOSTやDELETEなルーティングが失敗するのか？**

Railsの`link_to`ではオプションで`method: :post`や`method:
:delete`を指定することができます。`link_to`は`a`要素のタグを生成しますが、これらのオプションがあると`data-
method="post"`や`data-
method="delete"`という属性がつきます。これらの属性はHTML上では意味が無く、JavaScriptが動かない環境ではあってもなくても何も変わりません。

Rail6.0以降のデフォルトでは、各viweのテンプレート(/app/viwes/layouts/application.html.erb)のヘッダでapplication.jsをwebpacker経由で読み込んでいるはずです。このapplication.js(app/javascript/packs/application.js)のソースをみると`@rails/ujs`というモジュールを読み込んでいます。ここに、先程の`link_to`が作るタグに対する動作の秘密があります。

`@rails/ujs`にはRailsを補助するJavaScriptの集合です。`rails
yarn:install`した後であれば、node_modules/@rails/ujs/lib/assets/compiled/rails-
ujs.js(バージョン6.1.3の場合)でコードを確認できるでしょう。このJavaScriptは色々なことをしているのですが、その中の一つに`data-
method`がある`a`要素に対して、指定されたメソッドに動作を変えると言うものがあります。さらっとソースコードを見ただけですが、`form_with`等で`method`を指定した場合と同じ(form`要素一式を作る、または、Ajaxでメソッド指定)と同じ動作になるようにしているようです。

つまり、`@rails/ujs`によって、HTML上はただの`a`要素のリンクなのに、`form`を使ってPOSTやDELETEしたときと同じようなアクセスをブラウザにさせています。Railsはそれを判断して、メソッドにあったルーティングを行っているというわけです。逆に、`@rails/usj`が正しく動作しなければ、`a`要素のリンクは、ただのリンクなので、GETメソッド以外は起きず、ルーティングに失敗する場合があると言うことです。

* * *

ルーティングをGETでも受け取れるようにすれば、確かに解決できる場合もあります。しかし、JavaScriptが動いていないという根本的な問題を先延ばししているに過ぎません。`@rails/ujs`には他にも多くの補助する機能があり、それらで問題が起きる場合があるでしょう。ですので、ますは、JavaScirptが正しく動作するようになることを目指してください。


CC BY-SA 4.0
2021-07-04T05:22:55.593
77952
2021-07-04T05:22:55.593
7347
67119
answer