Rails is incorrectly sending redirect response as status 200

Viewed 965

My controller is used for both HTML and JSON requests, and behaves differently for each. Normally with the JSON request it will just render a JSON response, but if a special parameter is sent from the client, the JSON handler should perform a redirect as well:

# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
  return unless current_user == @post.user
  respond_to do |format|
    if @post.update(post_params)
      format.html {
        puts "HTML"
        redirect_to @post, notice: 'Post was successfully updated.'
      }
      format.json {
        puts "JS"
        if params[:redirect].to_s == "true"
          puts "REDIRECT"
          # ~~~~ THIS REDIRECT ISNT WORKING ~~~~
          redirect_to @post, notice: 'Post was successfully updated.'
        else
          puts "NO REDIRECT"
          render json: @post
        end
      }
    else
      format.html { render :edit }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

Through those puts statements I can verify that the request is coming in with format.json and params[:redirect].to_s == "true".

But I can see something peculiar in the Rails console logs:

Started PUT "/posts/2" for ::1 at 2020-07-08 01:43:06 -0700
Processing by PostsController#update as JSON
... other log output ...
JS
REDIRECT
Redirected to http://localhost:3000/posts/2
Completed 200 OK in 33ms (ActiveRecord: 13.4ms | Allocations: 18286)

Why is it using status code 200 after a redirect? And in the browser I can confirm that the response has code 200.

The browser is not redirecting after making the AJAX submission. And I suspect it has to do with this incorrect status code.

I tried explicitly passing status: 301 in the redirect_to call but it made no difference.


Update after writing out this question and thinking about it a little, I suppose it's probably easier to just manually do the redirect using the success handler the browser. But I'm still curious why this doesn't work.

1 Answers

According to this Medium article:

Apparently there is no clean way to do ajax redirects in Rails. The recommended way to do that is to send a regular 200 OK status code along with some special parameter signaling the javascript side that it is a REDIRECT.

The author suggests to make it clear with this workaround:

On the rails side, we create a helper that returns a response that has no content but just one header x_ajax_redirect_url and status code 302.

def ajax_redirect_to(url)
    head 302, x_ajax_redirect_url: url
end

A global ajax handler on the javascript side will intercept all ajax calls across the application and redirect on seeing the appropriate header construct.

$.ajaxSetup({
    statusCode: {
        302: function (response) {
            var redirect_url = response.getResponseHeader('X-Ajax-Redirect-Url');
            if (redirect_url != undefined) {
                window.location.pathname = redirect_url;
            }
        }
    }
});

This looks as the cleanest solution to me for now.

Related