Search code examples
ruby-on-railsrubyamp-html

Headers setting for AMP form in Rails


My AMP form in Rails is returning this error in the terminal :

Completed 406 Not Acceptable in 338ms (ActiveRecord: 54.1ms)
ActionController::UnknownFormat (InscriptionsController#create is missing a template for this request format and variant.
request.formats: ["application/json"]
request.variant: []):

And in the browser :

POST http://localhost:3000/inscriptions?__amp_source_origin=http%3A%2F%2Flocalhost%3A3000 406 (Not Acceptable)
Response must contain the AMP-Access-Control-Allow-Source-Origin header
Form submission failed: Error: Response must contain the AMP-Access-Control-Allow-Source-Origin header​​​
at bb.f.assert (https://cdn.ampproject.org/v0.js:21:319)
at y (https://cdn.ampproject.org/v0.js:26:213)
at https://cdn.ampproject.org/v0.js:179:339

However I did follow the CORS sample code provided by the official documentation https://amp.dev/documentation/guides-and-tutorials/learn/amp-caches-and-cors/amp-cors-requests?referrer=ampproject.org, and according to the AMP validation, everything is fine,

This is my controller :

def create
  @inscription = Inscription.new(inscription_params)
  @inscription.save
  subscriber_email = inscription_params[:email].downcase
  if Subscriber.where(email: subscriber_email).count == 0
    @subscriber = Subscriber.new
    @subscriber.email = subscriber_email
    @subscriber.save
    @new_subscriber = true
  else
    @subscriber = Subscriber.where(email: subscriber_email).first
    @new_subscriber = false
  end
 [...]
 respond_to do |format|
  format.html { redirect_to root_path, notice: "Merci ! Nous avons bien reçu votre inscription !" }
  format.js
  format.json {
    allowed_origins = ["https://example.com", "https://example.com.cdn.ampproject.org/", "http://example.com.amp.cloudflare.com", "https://cdn.ampproject.org"]
    allowed_source_origin = "https://example.com"
    source_origin = params[:__amp_source_origin]
    origin = request.headers["Origin"]
    response.set_header('Content-type', 'application/json')
    response.set_header('Access-Control-Allow-Credentials', 'true')
    response.set_header('Access-Control-Allow-Origin', origin)
    response.set_header('AMP-Access-Control-Allow-Source-Origin', source_origin)
    p response.headers
  }
  end
end

May you help me with this? I probably did something wrong with the headers.


Solution

  • I think the main issue here is not about unknown format, it's about template

    In the error you showed, it is clear that you are sending the proper Content-Type in your request

    request.formats: ["application/json"]
    

    And you've specified format.json in your controller action.

    So the only thing that comes to mind is that there is an issue with the response of your request. Now again looking at the error message, it says you are missing a template.

    ActionController::UnknownFormat (InscriptionsController#create is missing a template for this request format and variant

    The reason for this is that you are not returning any json response from the controller action

    format.json {
        allowed_origins = ["https://example.com", "https://example.com.cdn.ampproject.org/", "http://example.com.amp.cloudflare.com", "https://cdn.ampproject.org"]
        allowed_source_origin = "https://example.com"
        source_origin = params[:__amp_source_origin]
        origin = request.headers["Origin"]
        response.set_header('Content-type', 'application/json')
        response.set_header('Access-Control-Allow-Credentials', 'true')
        response.set_header('Access-Control-Allow-Origin', origin)
        response.set_header('AMP-Access-Control-Allow-Source-Origin', source_origin)
        p response.headers
      }
    

    You need to either render json directly from your controller action or if you don't do that then

    Rails tries to find an appropriate template for your controller action.

    and I think this is the main reason for this error.

    Solution:

    1. Add a render statement to your controller action

    In that case you can do something like this:

    format.json {
        allowed_origins = ["https://example.com", "https://example.com.cdn.ampproject.org/", "http://example.com.amp.cloudflare.com", "https://cdn.ampproject.org"]
        allowed_source_origin = "https://example.com"
        source_origin = params[:__amp_source_origin]
        origin = request.headers["Origin"]
        response.set_header('Content-type', 'application/json')
        response.set_header('Access-Control-Allow-Credentials', 'true')
        response.set_header('Access-Control-Allow-Origin', origin)
        response.set_header('AMP-Access-Control-Allow-Source-Origin', source_origin)
        p response.headers
    
        render json: {}, status: :ok
      }
    

    2. Create appropriate view file so that Rails can find it

    Rails will try to find a view file with as name as controller action in directory name of controller name. So in your case since the name of controller is InscriptionsController so your file should be

    app/views/inscriptions/create.json

    Suggestion

    In the link you provided, it is stated

    For requests from the allowed origins, our response will contain the following headers:

    Access-Control-Allow-Origin:

    AMP-Access-Control-Allow-Source-Origin:

    Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin

    But you are not setting the Access-Control-Expose-Headers in your create action which should be set in order for client to read additional response headers in CORS request.