Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discuss JSONP support #444

Closed
whitlockjc opened this issue Aug 19, 2015 · 11 comments
Closed

Discuss JSONP support #444

whitlockjc opened this issue Aug 19, 2015 · 11 comments
Labels
media and encoding Issues regarding media type support and how to encode data (outside of query/path params) OAI-scope

Comments

@whitlockjc
Copy link
Member

I just answered a question on StackOverflow about JSONP support in Swagger. During my answer, I realized that maybe Swagger could better handle JSONP. Let me give a little more detail.

To do JSONP in Swagger right now, you basically have to have a response that looks like this:

// ...
  200:
    description: My JSONP payload
    schema:
      type: 'string'
// ...

The reason for this is JSONP actually returns JavaScript code as a string. So for your Swagger API to truly describe your API, it must be of type string.

That being said, you lose a lot of validation options when this happens since as long as your response is a string, you've got a valid response. But what would be cool is if you could look into the JSONP response and validate its structure. Now how far you go with this depends as we have a few options:

  • Validate the JSONP structure/wrapper
  • Validate the response payload that gets unwrapped by JSONP-aware tooling

Both of these could be very useful. At first I was thinking of suggesting a jsonp string format but that alone will not be enough for the second option as to do payload validation you would need some schema reference/definition. So we would need some way to say "this is JSONP" and some other way to say "this is the schema to use to validate the JSONP data". How that looks can come after we decide whether this is something we want to pursue or not.

@fehguy
Copy link
Contributor

fehguy commented Aug 20, 2015

Yes, while it's waning a bit in popularity, JSONP is something we do want to support. In general:

  • We want to describe the payload contents. This is different from validate, but they should be compatible
  • The transfer mechanism for the payload is text.
  • The query parameter callback is changing the response behavior completely

I do think this is outside 2.0. Do we really want a different schema for jsonp? It's the same payload just in a different response type, and wrapped.

@JacquesPerrault
Copy link

So, if a response type is string, e.g.

definitions:
 getreposResponse:
    type: string
    required:
      - success
      - data
    properties:
      success:
        type: boolean
      data:
        type: array
        items:
          $ref: "#/definitions/Repo"

Does that mean the validator doesn't perform any further validation on the rest of the definition (such as success and data in the example above?

@whitlockjc touched upon what I was getting at, although I'm not sure the JSONP wrapper/structure needs validation. For example, here's a snippet of my code for handling JSONP:

client side

var url = '/getrepos';
var jqxhr = $.ajax({
  url: url,
  dataType: "jsonp",
  crossDomain: true,
  data: '',
  success: callback

var callback = function(data) {
  result = $.parseJSON(data);
  if (result.success) {
      //do something
  } else {
      errorcallback(data)
  }
}

var errorcallback = function(error) {
  // handle the error
}

server side

module.exports = {
        getrepos: getrepos
}

function getrepos(req, res) {
  console.log('=====================================');
  console.log('GET ALL REPOS =======================');
  console.log('=====================================');
  var callback = req.swagger.params.callback.value;
  if (typeof callback === 'undefined') callback = 'callback' + Date.now();
  if (allrepos.length === 0 ) {
    var error = {'success': false, 'message': 'Please load repository data first.', 'code': 120 };
    res.status(200).jsonp(JSON.stringify(error));
  } else {
    var retVal = {};
    retVal.success = true;
    retVal.data = JSON.stringify(allrepos);
    res.status(200).jsonp(JSON.stringify(retVal));
  }
}

request
localhost:3000/getrepos?callback=jQuery111106460774131119251_1439402109118

response

typeof jQuery111106460774131119251_1439402109118 === 'function' && jQuery111106460774131119251_1439402109118("
{
    \"success\": true,
    \"data\": \"[
        {
            \\\"pull_count\\\": 30,
            \\\"pushed_at\\\": \\\"\\\",
            \\\"release_count\\\": 27,
            \\\"branch_count\\\": 11,
            \\\"contributors_count\\\": 30
        }
    ]\"
}
");

Express handles JSONP wrapper creation, so the developer doesn't really have to worry about the wrapper being invalid, but it would be nice if swagger was able to validate the payload as more than just a string, and then wrap it.

@whitlockjc
Copy link
Member Author

If you have a type of string, you do not define properties as that property only applies for type object. That's the point I was making is that when you want to support JSONP, the wrapper (what is actually returned from the API) is a string of JavaScript code that wraps your actual JSON response structure. So by using type string, you lose the ability to validate that the JSONP response actually is valid and that its data attribute adheres to the object structure defined at #/definitions/Repo.

@JacquesPerrault
Copy link

Thanks, that's what I figured you were implying, but I opted to keep the definitions, because it is still helpful to be able to have swagger create the documentation for API consumers.

@akuckartz
Copy link

Is CORS supported ?

@loretoparisi
Copy link

loretoparisi commented Aug 1, 2016

Has this issue been resolved now?
If I use the endpoint type set to string, will the dataType of XMLHttpRequest object set to jsonp?
Also, since the javascript client is superagent does this mean that the superagent code must support jsonp in the browser?

@loretoparisi
Copy link

Despite of a formal solution, since for client code the issue is with superagent too, what I did was to add json support in the generated ApiClient.js module in the api export

exports.prototype.callApi = function callApi(path, httpMethod, pathParams,
      queryParams, headerParams, formParams, bodyParam, authNames, contentTypes, accepts,
      returnType, callback) 

that is the following

/////////JSONP
    let serialise = function(obj) {
      if (typeof obj != 'object') return obj;
      let pairs = [];
      for (let key in obj) {
        if (null != obj[key]) {
          pairs.push(encodeURIComponent(key)
            + '=' + encodeURIComponent(obj[key]));
        }
      }
      return pairs.join('&');
    }
    let jsonp = function(requestOrConfig) {
      var reqFunc = function(request) {
        // In case this is in nodejs, run without modifying request
        if (typeof window == 'undefined') return request;

        request.end = end.bind(request)(requestOrConfig);
        return request;
      };
      // if requestOrConfig is request
      if(typeof requestOrConfig.end == 'function') {
        return reqFunc(requestOrConfig);
      } else {
        return reqFunc;
      }
    };
    jsonp.callbackWrapper = function(data) {
      let err = null;
      let res = {
        body: data
      };
      clearTimeout(this._jsonp.timeout);

      this._jsonp.callback.call(this, err, res);
    };
    jsonp.errorWrapper = function() {
      const err = new Error('404 NotFound');
      this._jsonp.callback.call(this, err, null);
    };
    let end = function(config = { timeout: 1000 }) {
      return function(callback) {

        let timeout = setTimeout(
          jsonp.errorWrapper.bind(this),
          config.timeout
        );

        this._jsonp = {
          callbackParam: config.callbackParam || 'callback',
          callbackName:  config.callbackName || 'superagentCallback' + new Date().valueOf() + parseInt(Math.random() * 1000),
          callback:             callback,
          timeout:        timeout
        };

        window[this._jsonp.callbackName] = jsonp.callbackWrapper.bind(this);

        let params = {
          [this._jsonp.callbackParam]: this._jsonp.callbackName
        };

        this._query.push(serialise(params));
        let queryString = this._query.join('&');

        let s = document.createElement('script');
        let separator = (this.url.indexOf('?') > -1) ? '&': '?';
        let url = this.url + separator + queryString;

        s.src = url;
        document.getElementsByTagName('head')[0].appendChild(s);

        return this;
      }
    };
    /////////JSONP
   request.use(jsonp)

At this point I get it working, but with both the error and the data callback fired:

Error: 404 NotFound(…) null null
(index):24 Error: 404 NotFound(…)callback @ (index):24(anonymous function) @ bundle.js:492jsonp.errorWrapper @ bundle.js:409
(index):22 null exports {message: exports} Object {body: Object}
(index):26 API called successfully. Returned data: [object Object]

@ePaul
Copy link
Contributor

ePaul commented Aug 3, 2016

@whitlockjc I'm not sure that describing this as type: string is actually right – it seems to imply that we have a JSON string in the payload, (i.e. something like "..."), while actually we have a completely different content type (a piece of JavaScript). I guess type: file would be more correct here.

(And yes, this needs explicit support in the spec.)

@silkentrance
Copy link

Would it not be better to just state the correct content type for the response? See https://stackoverflow.com/questions/111302/best-content-type-to-serve-jsonp

@handrews
Copy link
Member

@OAI/tsc JSONP is pretty thoroughly discouraged in favor of CORS at this point- are we still considering support?

@handrews handrews added the media and encoding Issues regarding media type support and how to encode data (outside of query/path params) label Jan 29, 2024
@handrews
Copy link
Member

Silence == nope, we're not considering it due to its obsolescence (due to security risks). Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
media and encoding Issues regarding media type support and how to encode data (outside of query/path params) OAI-scope
Projects
None yet
Development

No branches or pull requests

8 participants