Rails response content-type negotiation 的一些问题

hmsk86 · November 11, 2025 · Last by rickluck90 replied at December 22, 2025 · 258 hits

学习 controller 的 respond_to 方法时遇到的一个问题

# app/controllers/test_controller.rb
class TestController < ApplicationController
  def create
    params = self.params.expect user: [ :name, :email ]
    name = params[:name]
    email = params[:email]
    respond_to do |format|
      format.html { render html: "这是html: name=#{name} email=#{email}" }
      format.json { render json: { msg: "这是json", **params } }
    end
  end
end

# config/route.rb
Rails.application.routes.draw do
  post "/test", to: "test#create"
end

用 httpie 发送 post 请求,注意请求头带有Accept: application/json, */*;q=0.5,按理说应该返回 json 响应,但是 rails 返回了 html 响应

$ http POST http://localhost:3000/test user[name]=foo user[email][email protected] --verbose
POST /test HTTP/1.1
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 53
User-Agent: HTTPie/3.2.4
Accept: application/json, */*;q=0.5
Content-Type: application/json
Host: localhost:3000

{"user": {"name": "foo", "email": "[email protected]"}}

HTTP/1.1 200 OK
x-frame-options: SAMEORIGIN
x-xss-protection: 0
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
content-type: text/html; charset=utf-8
etag: W/"644544dc838ec7f5f9d146a6efaf52af"
cache-control: max-age=0, private, must-revalidate
x-request-id: ef6be23e-f655-4368-ad0e-5561c1b975ac
x-runtime: 0.002610
server-timing: start_processing.action_controller;dur=0.01, render_template.action_view;dur=0.01, process_action.action_controller;dur=1.32
content-length: 42

这是html: name=foo [email protected]

Accept: application/json, */*;q=0.5改成Accept: application/json就行了,也就是说,如果请求头的Accept带有*/*,那么 rails 一律返回 html 响应,无视优先级

$ http POST http://localhost:3000/test user[name]=foo user[email][email protected] Accept:'application/json' --verbose
POST /test HTTP/1.1
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 53
User-Agent: HTTPie/3.2.4
Accept: application/json
Content-Type: application/json
Host: localhost:3000

{"user": {"name": "foo", "email": "[email protected]"}}

HTTP/1.1 200 OK
x-frame-options: SAMEORIGIN
x-xss-protection: 0
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
content-type: application/json; charset=utf-8
vary: Accept
etag: W/"f1ca37f45944a0eb8cc6bc093b6fc5c8"
cache-control: max-age=0, private, must-revalidate
x-request-id: cdf64112-4d57-4569-af76-ce712b165fe9
x-runtime: 0.001742
server-timing: start_processing.action_controller;dur=0.01, process_action.action_controller;dur=0.31
content-length: 59

{"msg":"这是json","name":"foo","email":"[email protected]"}

Your knowledge is very useful. Like you, I often look for the game redactle to pass the time. You should try it at least once to see what I mean.

In Rails, respond_to is used to return different formats (HTML, JSON, etc.) from the same action. But for now, check Plumber Prince George.

You need to Sign in before reply, if you don't have an account, please Sign up first.