Rails响应格式 #

一、响应格式概述 #

1.1 支持的格式 #

Rails支持多种响应格式:

格式 说明 Content-Type
HTML 网页 text/html
JSON API数据 application/json
XML XML数据 application/xml
JS JavaScript application/javascript
CSV 表格数据 text/csv
PDF PDF文档 application/pdf

1.2 格式协商 #

Rails通过以下方式确定响应格式:

  1. URL后缀:/articles.json
  2. Accept头:Accept: application/json
  3. 参数:?format=json

二、respond_to #

2.1 基本用法 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    respond_to do |format|
      format.html
      format.json { render json: @article }
      format.xml  { render xml: @article }
    end
  end
end

2.2 格式选项 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    respond_to do |format|
      format.html { render :show }
      format.json { render json: @article, status: :ok }
      format.xml  { render xml: @article, root: 'article' }
      format.js   { render js: "alert('Hello')" }
      format.text { render plain: @article.body }
    end
  end
end

2.3 默认格式 #

ruby
# config/routes.rb
Rails.application.routes.draw do
  resources :articles, defaults: { format: :json }
end

# 或在控制器中
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
    respond_to do |format|
      format.any { render json: @articles }
    end
  end
end

三、JSON响应 #

3.1 基本JSON渲染 #

ruby
# app/controllers/api/v1/articles_controller.rb
module Api
  module V1
    class ArticlesController < ApplicationController
      def index
        @articles = Article.all
        render json: @articles
      end
      
      def show
        @article = Article.find(params[:id])
        render json: @article
      end
      
      def create
        @article = Article.new(article_params)
        
        if @article.save
          render json: @article, status: :created
        else
          render json: { errors: @article.errors }, status: :unprocessable_entity
        end
      end
    end
  end
end

3.2 自定义JSON #

ruby
# app/controllers/api/v1/articles_controller.rb
module Api
  module V1
    class ArticlesController < ApplicationController
      def show
        @article = Article.find(params[:id])
        
        render json: {
          id: @article.id,
          title: @article.title,
          body: @article.body,
          author: {
            id: @article.author.id,
            name: @article.author.name
          },
          created_at: @article.created_at.iso8601
        }
      end
    end
  end
end

3.3 使用Jbuilder #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    respond_to do |format|
      format.json
    end
  end
end

# app/views/articles/show.json.jbuilder
json.id @article.id
json.title @article.title
json.body @article.body
json.author do
  json.id @article.author.id
  json.name @article.author.name
end
json.created_at @article.created_at.iso8601

3.4 JSON选项 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
    
    render json: @articles,
      only: [:id, :title, :created_at],
      methods: [:summary],
      include: {
        author: { only: [:id, :name] }
      }
  end
end

四、XML响应 #

4.1 基本XML渲染 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    render xml: @article
  end
end

4.2 自定义XML #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    render xml: @article.to_xml(
      only: [:id, :title, :body],
      include: {
        author: { only: [:id, :name] }
      }
    )
  end
end

五、JavaScript响应 #

5.1 JS模板 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)
    
    respond_to do |format|
      if @article.save
        format.html { redirect_to @article }
        format.js   # 渲染 create.js.erb
      else
        format.html { render :new }
        format.js   # 渲染 create.js.erb
      end
    end
  end
end

# app/views/articles/create.js.erb
$('#articles').append('<%= j render @article %>');
$('#article_form').trigger('reset');

5.2 内联JavaScript #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def update
    @article = Article.find(params[:id])
    @article.update(article_params)
    
    render js: "alert('Article updated!')"
  end
end

六、CSV响应 #

6.1 基本CSV渲染 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
    
    respond_to do |format|
      format.html
      format.csv do
        send_data generate_csv(@articles),
          filename: "articles-#{Date.today}.csv",
          type: 'text/csv'
      end
    end
  end
  
  private
  
  def generate_csv(articles)
    CSV.generate(headers: true) do |csv|
      csv << ['ID', 'Title', 'Author', 'Created At']
      
      articles.each do |article|
        csv << [article.id, article.title, article.author.name, article.created_at]
      end
    end
  end
end

6.2 CSV模板 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
    
    respond_to do |format|
      format.csv { render csv: @articles }
    end
  end
end

# app/views/articles/index.csv.erb
<%= CSV.generate_line(['ID', 'Title', 'Author', 'Created At']) %>
<% @articles.each do |article| %>
<%= CSV.generate_line([article.id, article.title, article.author.name, article.created_at]) %>
<% end %>

七、PDF响应 #

7.1 使用Prawn #

ruby
# Gemfile
gem 'prawn'

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    respond_to do |format|
      format.html
      format.pdf do
        pdf = Prawn::Document.new
        pdf.text @article.title, size: 24, style: :bold
        pdf.text @article.body
        send_data pdf.render,
          filename: "#{@article.title}.pdf",
          type: 'application/pdf'
      end
    end
  end
end

7.2 使用Wicked PDF #

ruby
# Gemfile
gem 'wicked_pdf'

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    respond_to do |format|
      format.html
      format.pdf do
        render pdf: @article.title,
               template: 'articles/show',
               layout: 'pdf'
      end
    end
  end
end

八、文件下载 #

8.1 send_data #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def download
    content = generate_content
    
    send_data content,
      filename: 'articles.txt',
      type: 'text/plain',
      disposition: 'attachment'
  end
end

8.2 send_file #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def download
    file_path = Rails.root.join('public', 'files', 'document.pdf')
    
    send_file file_path,
      filename: 'document.pdf',
      type: 'application/pdf',
      disposition: 'attachment'
  end
end

8.3 Active Storage下载 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def download_attachment
    @article = Article.find(params[:id])
    
    redirect_to @article.attachment.url,
      disposition: 'attachment'
  end
end

九、API响应模式 #

9.1 统一响应格式 #

ruby
# app/controllers/api/v1/base_controller.rb
module Api
  module V1
    class BaseController < ApplicationController
      private
      
      def render_success(data = nil, message: 'Success')
        render json: {
          success: true,
          message: message,
          data: data
        }
      end
      
      def render_error(message, status: :bad_request, errors: nil)
        render json: {
          success: false,
          message: message,
          errors: errors
        }, status: status
      end
    end
  end
end

# app/controllers/api/v1/articles_controller.rb
module Api
  module V1
    class ArticlesController < BaseController
      def create
        @article = Article.new(article_params)
        
        if @article.save
          render_success(@article, message: 'Article created')
        else
          render_error('Validation failed', errors: @article.errors)
        end
      end
    end
  end
end

9.2 分页响应 #

ruby
# app/controllers/api/v1/articles_controller.rb
module Api
  module V1
    class ArticlesController < BaseController
      def index
        @articles = Article.page(params[:page]).per(params[:per_page] || 20)
        
        render json: {
          articles: @articles,
          pagination: {
            current_page: @articles.current_page,
            total_pages: @articles.total_pages,
            total_count: @articles.total_count,
            per_page: @articles.limit_value
          }
        }
      end
    end
  end
end

十、内容协商 #

10.1 自定义MIME类型 #

ruby
# config/initializers/mime_types.rb
Mime::Type.register 'application/vnd.api+json', :api_json
Mime::Type.register 'application/vnd.myapp.v1+json', :v1_json

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    respond_to do |format|
      format.json { render json: @article }
      format.api_json { render json: @article, adapter: :json_api }
    end
  end
end

10.2 API版本控制 #

ruby
# app/controllers/api/v1/articles_controller.rb
module Api
  module V1
    class ArticlesController < ApplicationController
      def index
        @articles = Article.all
        render json: @articles
      end
    end
  end
end

# app/controllers/api/v2/articles_controller.rb
module Api
  module V2
    class ArticlesController < ApplicationController
      def index
        @articles = Article.includes(:author).all
        render json: @articles, include: :author
      end
    end
  end
end

十一、总结 #

11.1 核心要点 #

要点 说明
respond_to 多格式响应
JSON渲染 API开发常用
文件下载 send_data/send_file
格式协商 Accept头和URL后缀
统一响应 API最佳实践

11.2 下一步 #

现在你已经掌握了响应格式,接下来让我们学习 ERB模板,深入了解Rails的视图系统!

最后更新:2026-03-28