Search code examples
ruby-on-railsjsontin-can-apiarticulate-storyline

How can one parse this JSON block?


I am working the TinCan API which is sending non-json objects as a JSON request.

An example of the Request Payload in said request :

AWSAccessKeyId=secret
&Authorization=secret
&activityId=61XkSYC1ht2%5Fcourse%5Fid
&Expires=1395864543
&Content%2DType=application%2Fjson
&actor=null
&registration=760e3480%2Dba55%2D4991%2D94b0%2D01820dbd23a2
&stateId=resume
&Signature=ZNYa7WTtO5rWx%2FAs%2FuFxTQkiYdc%3D

Their documentation explains in a paraphrased form that "The data is being sent as described in section 7.8 Cross Origin Requests of the XAPI spec, where all headers and content are being included as form parameters."

Which you can see is true from the Content key in the example above. That key and its children can be decoded and be parsed as JSON.

But because the initial request is application/json, my app runs into a JSON parse error.

That being said, is there some way to set up the server, or my controllers to accept these CORS requests so that I can properly parse them and use their information?


Solution

  • If you're up against the wall and have no way of changing the completely broken client, you might want to try and gracefully recover here.

    Usually you can add a filter in your config.ru file for your application that will re-write the damaged headers:

    use TinCanFixer
    

    Then you write a Rack handler:

    class TinCanFixer
      def initialize(app)
        @app = app
      end
    
      def call(env)
        case (env["CONTENT_TYPE"])
        when "application/json"
          # Check that it's actually JSON
          unless (env["rack.input"].match(/^\{\[/))
            env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
          end
        end
    
        @app.call(env)
      end
    end
    

    Untested but should, in principle, sniff out non-JSON content and reassign the rack.input header which dictates content type.