简体   繁体   中英

Rails, respond_to blocks and |format|

Rails scaffold generated the following:

respond_to do |format|
  if @student.save
    format.html { redirect_to @student, notice => 'Student was successfully created.' }
    format.json { render :show, status: :created, location: @student }
  else
    format.html { render :new }
    format.json { render json: @student.errors, status: :unprocessable_entity }
  end
end

After reading this I understand how the respond_to is working (sort of), but I don't get what format is doing. Shouldn't it be either format.html or format.json and not both ? What are these two lines actually doing?

format.html { render :new }
format.json { render json: @student.errors, status: :unprocessable_entity }

Is there an implied if in there? Is it something like

if (format == html) {}
if (format == json) {}

Side note: Why does update require the respond_to block while show will handle /students/1.json or /students/1 without any logic at all?

format is a local variable that respond_to yields . When you do format.html {} you are actually registering a callback block for a format.

Rails goes through the registered formats and tries to find a compatible format to the MIME type in the request . If there is no handler it will raise an error.

This could be explained as something like using syntactic sugar on top of a case statement (the Ruby equivalent of a switch statement). But the analogy is not completely accurate since Rails does a bit of work in matching the request type.

Also the code inside your block is not executed when the format.html block is registered (as it would be if it was just a conditional statement) but rather when respond_to finishes or not at all if you are using for example E-Tag caching.

Why does update require the respond_to block while show will handle /students/1.json or /students/1 without any logic at all?

Rails handles many actions by using a convention over configuration approach and guessing the intent of the action.

def PostsController < ApplicationController
   def index
     # rails auto-magically fills in the controller with something 
     # like this
     @posts = Post.all
     respond_to do |format|
       format.html { render :index }
       format.json { render json: @posts }
     end 
   end

   def show
     # convention over configuration is awesome!
     @post = Post.find(params[:id])
     respond_to do |format|
       format.html { render :show }
       format.json { render json: @post }
     end
   end

   def new 
     @post = Post.new
     render :new
   end

   def edit 
     @post = Post.find(params[:id])
     render :edit
   end   
end

Rails assumes that there is a resource with the same name as the controller and auto-magically fills in the controller action. It also assumes there is a view in app/views/posts/(:action).html.[erb|haml|slim|jbuilder] . This is known as implicit rendering .

The comments show roughly what action rails attempts.

It does not fill in actions which operate on data (create, update, destroy) since the actual implementation can vary greatly and it's hard to make useful guesses.

Well, it depends on the format of the request. If a request demands HTML from the server, format.html block will be executed, and in the same way, if a request demands JSON format, format.json will be executed.

Rails will automatically(read: magically) handle the if (format == html) part for you. All you have to do is fill in the blanks. Same way, you can write a block for XML starting with format.xml .

And for the side note, I think you have said it otherwise. update method doesn't require respond_to block, while show requires. And the reason is very simple: update method is there to update the Model, and then, redirect you to somewhere, while show will always return you something. In your case, /students/1 will return you the first student created in the database, and the response will be HTML, while /students/1.json will return you the same result, but response will be JSON this time.

Well you could very well replace 'format' with 'foo' or 'banana' or whatever you want. It is just the variable name in this case because the variable that is sent to your block by respond_to is passing along the format as requested by the incoming http request's Accept header.

Sometimes you'll see 422 "Unacceptable" errors in your logs because you are receiving a request with an Accept header that does not request a mime type your app knows about.

As it is, your callers should be using a browser or be a JSON consumer sending the proper headers to receive responses from the boilerplate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM