如何在Rails API-only应用中保持会话持久化

huangapple go评论143阅读模式
英文:

How to persist session in rails API only app

问题

我对Ruby on Rails还不太熟悉,但我可以帮你翻译你提供的代码和问题。以下是翻译的内容:

我对Ruby on Rails还不太熟悉,我想在Rails 6的API应用中添加会话(sessions)功能,以便在会话中获取current_cart的ID。如果会话中不存在该ID,则创建一个新的购物车并将ID存储在会话中。

  • 我想知道是否有比添加会话更好的选择?例如使用JWT令牌。
  • 我正在遵循Rails敏捷Web开发书籍的指导,但我只在Rails API模式下进行操作。

以下是我的application.rb文件:

  1. # frozen_string_literal: true
  2. require_relative 'boot'
  3. require 'rails'
  4. # Pick the frameworks you want:
  5. require 'active_model/railtie'
  6. require 'active_job/railtie'
  7. require 'active_record/railtie'
  8. require 'active_storage/engine'
  9. require 'action_controller/railtie'
  10. require 'action_mailer/railtie'
  11. require 'action_mailbox/engine'
  12. require 'action_text/engine'
  13. require 'action_view/railtie'
  14. require 'action_cable/engine'
  15. require 'sprockets/railtie'
  16. # require "rails/test_unit/railtie"
  17. # Require the gems listed in Gemfile, including any gems
  18. # you've limited to :test, :development, or :production.
  19. Bundler.require(*Rails.groups)
  20. module DepotApi
  21. class Application < Rails::Application
  22. # Initialize configuration defaults for originally generated Rails version.
  23. config.load_defaults 6.1
  24. # skip verifying users on logout
  25. config.to_prepare do
  26. Devise::SessionsController.skip_before_action :verify_signed_out_user, only: :destroy
  27. end
  28. # Configuration for the application, engines, and railties goes here.
  29. #
  30. # These settings can be overridden in specific environments using the files
  31. # in config/environments, which are processed later.
  32. #
  33. # config.time_zone = "Central Time (US & Canada)"
  34. config.eager_load_paths << Rails.root.join('lib')
  35. # Only loads a smaller set of middleware suitable for API only apps.
  36. # Middleware like session, flash, cookies can be added back manually.
  37. # Skip views, helpers and assets when generating a new resource.
  38. config.api_only = true
  39. config.app_generators.scaffold_controller = :scaffold_controller
  40. # Middleware for ActiveAdmin
  41. config.middleware.use Rack::MethodOverride
  42. config.middleware.use ActionDispatch::Flash
  43. config.middleware.use ActionDispatch::Cookies
  44. config.middleware.use ActionDispatch::Session::CookieStore
  45. end
  46. end

以下是我的Line_items控制器:

  1. # frozen_string_literal: true
  2. class LineItemsController < ApiController
  3. include CurrentCart
  4. before_action :set_cart, only: [:create]
  5. before_action :set_line_item, only: %i[show update destroy]
  6. # GET /line_items
  7. # GET /line_items.json
  8. def index
  9. @line_items = LineItem.all
  10. end
  11. # GET /line_items/1
  12. # GET /line_items/1.json
  13. def show; end
  14. # POST /line_items
  15. # POST /line_items.json
  16. def create
  17. product = Product.find(params[:product_id])
  18. @line_item = @cart.add_product(product)
  19. if @line_item.save
  20. render :show, status: :created, location: @line_item
  21. else
  22. render json: @line_item.errors, status: :unprocessable_entity
  23. end
  24. end
  25. # Increasing quantity
  26. def add_quantity
  27. @line_item.quantity += 1
  28. @line_item.save
  29. end
  30. # decreasing quantity
  31. def reduce_quantity
  32. @line_item.quantity > 1
  33. @line_item.quantity -= 1
  34. @line_item.save
  35. end
  36. # PATCH/PUT /line_items/1
  37. # PATCH/PUT /line_items/1.json
  38. def update
  39. if @line_item.update(line_item_params)
  40. render :show, status: :ok, location: @line_item
  41. else
  42. render json: @line_item.errors, status: :unprocessable_entity
  43. end
  44. end
  45. # DELETE /line_items/1
  46. # DELETE /line_items/1.json
  47. def destroy
  48. @line_item.destroy
  49. end
  50. private
  51. # Use callbacks to share common setup or constraints between actions.
  52. def set_line_item
  53. @line_item = LineItem.find(params[:id])
  54. end
  55. # Only allow a list of trusted parameters through.
  56. def line_item_params
  57. params.require(:line_item).permit(:product_id)
  58. end
  59. end

@cart的值由我包含的当前模块设置。

以下是我的CurrentCart模块,我在其中设置了会话:

  1. module CurrentCart
  2. private
  3. def set_cart
  4. @cart = Cart.find(session[:cart_id])
  5. rescue ActiveRecord::RecordNotFound
  6. @cart = Cart.create
  7. session[:cart_id] = @cart.id
  8. end
  9. end

除了activeadmin之外,我的所有API控制器都继承自这个ApiController:

  1. class ApiController < ActionController::API
  2. end

以下是我的application controller:

  1. # frozen_string_literal: true
  2. class ApplicationController < ActionController::Base
  3. # Prevent CSRF attacks by using :null_session
  4. protect_from_forgery with: :null_session
  5. before_action :configure_permitted_parameters, if: :devise_controller?
  6. rescue_from CanCan::AccessDenied do |_exception|
  7. render json: { error: 'Access denied' }, status: :forbidden
  8. end
  9. def authenticate_admin_user!
  10. if admin_user_signed_in?
  11. admin_root_path
  12. else
  13. new_admin_user_session_path
  14. end
  15. end
  16. protected
  17. def configure_permitted_parameters
  18. devise_parameter_sanitizer.permit(:sign_up, keys: %i[user_name email role])
  19. devise_parameter_sanitizer.permit(:sign_in, keys: %i[user_name email role])
  20. end
  21. end

希望这可以帮助到你!如果你有任何其他问题,请随时问我。

英文:

I am pretty new to ruby on rails, I want to add sessions back to rails 6 API app such that I get the current_cart id in session, if the the id does not exist in session, it goes on to create a new cart and store the id in session.

  • I would like to know if there is another better option than adding sessions back? i.e. like using JWT tokens.
  • I am following rails agile web development book, but I did it in rails API mode only.

Below is my application.rb

  1. # frozen_string_literal: true
  2. require_relative &#39;boot&#39;
  3. require &#39;rails&#39;
  4. # Pick the frameworks you want:
  5. require &#39;active_model/railtie&#39;
  6. require &#39;active_job/railtie&#39;
  7. require &#39;active_record/railtie&#39;
  8. require &#39;active_storage/engine&#39;
  9. require &#39;action_controller/railtie&#39;
  10. require &#39;action_mailer/railtie&#39;
  11. require &#39;action_mailbox/engine&#39;
  12. require &#39;action_text/engine&#39;
  13. require &#39;action_view/railtie&#39;
  14. require &#39;action_cable/engine&#39;
  15. require &#39;sprockets/railtie&#39;
  16. # require &quot;rails/test_unit/railtie&quot;
  17. # Require the gems listed in Gemfile, including any gems
  18. # you&#39;ve limited to :test, :development, or :production.
  19. Bundler.require(*Rails.groups)
  20. module DepotApi
  21. class Application &lt; Rails::Application
  22. # Initialize configuration defaults for originally generated Rails version.
  23. config.load_defaults 6.1
  24. # skip verifying users on logout
  25. config.to_prepare do
  26. Devise::SessionsController.skip_before_action :verify_signed_out_user, only: :destroy
  27. end
  28. # Configuration for the application, engines, and railties goes here.
  29. #
  30. # These settings can be overridden in specific environments using the files
  31. # in config/environments, which are processed later.
  32. #
  33. # config.time_zone = &quot;Central Time (US &amp; Canada)&quot;
  34. config.eager_load_paths &lt;&lt; Rails.root.join(&#39;lib&#39;)
  35. # Only loads a smaller set of middleware suitable for API only apps.
  36. # Middleware like session, flash, cookies can be added back manually.
  37. # Skip views, helpers and assets when generating a new resource.
  38. config.api_only = true
  39. config.app_generators.scaffold_controller = :scaffold_controller
  40. # Middleware for ActiveAdmin
  41. config.middleware.use Rack::MethodOverride
  42. config.middleware.use ActionDispatch::Flash
  43. config.middleware.use ActionDispatch::Cookies
  44. config.middleware.use ActionDispatch::Session::CookieStore
  45. end
  46. end

Below is my Line_items controller

  1. # frozen_string_literal: true
  2. class LineItemsController &lt; ApiController
  3. include CurrentCart
  4. before_action :set_cart, only: [:create]
  5. before_action :set_line_item, only: %i[show update destroy]
  6. # GET /line_items
  7. # GET /line_items.json
  8. def index
  9. @line_items = LineItem.all
  10. end
  11. # GET /line_items/1
  12. # GET /line_items/1.json
  13. def show; end
  14. # POST /line_items
  15. # POST /line_items.json
  16. def create
  17. product = Product.find(params[:product_id])
  18. @line_item = @cart.add_product(product)
  19. if @line_item.save
  20. render :show, status: :created, location: @line_item
  21. else
  22. render json: @line_item.errors, status: :unprocessable_entity
  23. end
  24. end
  25. # Increasing quantity
  26. def add_quantity
  27. @line_item.quantity += 1
  28. @line_item.save
  29. end
  30. # decreasing quantity
  31. def reduce_quantity
  32. @line_item.quantity &gt; 1
  33. @line_item.quantity -= 1
  34. @line_item.save
  35. end
  36. # PATCH/PUT /line_items/1
  37. # PATCH/PUT /line_items/1.json
  38. def update
  39. if @line_item.update(line_item_params)
  40. render :show, status: :ok, location: @line_item
  41. else
  42. render json: @line_item.errors, status: :unprocessable_entity
  43. end
  44. end
  45. # DELETE /line_items/1
  46. # DELETE /line_items/1.json
  47. def destroy
  48. @line_item.destroy
  49. end
  50. private
  51. # Use callbacks to share common setup or constraints between actions.
  52. def set_line_item
  53. @line_item = LineItem.find(params[:id])
  54. end
  55. # Only allow a list of trusted parameters through.
  56. def line_item_params
  57. params.require(:line_item).permit(:product_id)
  58. end
  59. end
  • The value of @cart is set by the current module I have included.

Below is my Currentcart module where I set the session from

  1. module CurrentCart
  2. private
  3. def set_cart
  4. @cart = Cart.find(session[:cart_id])
  5. rescue ActiveRecord::RecordNotFound
  6. @cart = Cart.create
  7. session[:cart_id] = @cart.id
  8. end
  9. end

All my API controllers are extending from this apiController except activeadmin

  1. class ApiController &lt; ActionController::API
  2. end

Below is my application controller

  1. # frozen_string_literal: true
  2. class ApplicationController &lt; ActionController::Base
  3. # Prevent CSRF attacks by using :null_session
  4. protect_from_forgery with: :null_session
  5. before_action :configure_permitted_parameters, if: :devise_controller?
  6. rescue_from CanCan::AccessDenied do |_exception|
  7. render json: { error: &#39;Access denied&#39; }, status: :forbidden
  8. end
  9. def authenticate_admin_user!
  10. if admin_user_signed_in?
  11. admin_root_path
  12. else
  13. new_admin_user_session_path
  14. end
  15. end
  16. protected
  17. def configure_permitted_parameters
  18. devise_parameter_sanitizer.permit(:sign_up, keys: %i[user_name email role])
  19. devise_parameter_sanitizer.permit(:sign_in, keys: %i[user_name email role])
  20. end
  21. end

答案1

得分: 0

我终于找到了一个解决方案,让两个应用程序连接起来,一个是create react app,另一个是rails api。首先,我需要在rails api中添加回话(sessions)的支持。

  1. config.api_only = true
  2. config.app_generators.scaffold_controller = :scaffold_controller
  3. # Middleware for ActiveAdmin
  4. config.middleware.use Rack::MethodOverride
  5. config.middleware.use ActionDispatch::Flash
  6. config.middleware.use ActionDispatch::Cookies
  7. config.middleware.use ActionDispatch::Session::CookieStore

然后,在create react app中,我在package.json中添加了以下设置:

  1. "proxy": "http://127.0.0.1:4000"

接下来,我将前端(create react app)的所有路由更改如下:

http://127.0.0.1.400/products更改为/products

这样,你就可以在前端获取会话中的任何内容了。

英文:
  • I finally a solution, for the two apps to connect, the create react app and a rails api i needed to first add back sessions to the rails api
  1. config.api_only = true
  2. config.app_generators.scaffold_controller = :scaffold_controller
  3. # Middleware for ActiveAdmin
  4. config.middleware.use Rack::MethodOverride
  5. config.middleware.use ActionDispatch::Flash
  6. config.middleware.use ActionDispatch::Cookies
  7. config.middleware.use ActionDispatch::Session::CookieStore
  • then in create react app i added this setting in
    package.json
    &quot;proxy&quot;: &quot;http://127.0.0.1:4000&quot;

  • Then all my routes on the front end (create react app) where changed as follow
    http://127.0.0.1.400/products to /products

  • Their you can get anything from session on the front end

huangapple
  • 本文由 发表于 2023年8月8日 23:05:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76860850.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定