Code Monkey home page Code Monkey logo

xhr's Introduction

xhr

Join the chat at https://gitter.im/naugtur-xhr/Lobby

A small XMLHttpRequest wrapper. Designed for use with browserify, webpack etc.

API is a subset of request so you can write code that works in both node.js and the browser by using require('request') in your code and telling your browser bundler to load xhr instead of request.

For browserify, add a browser field to your package.json:

"browser": {
  "request": "xhr"
}

For webpack, add a resolve.alias field to your configuration:

"resolve": {
  "alias": {
    "request$": "xhr"
  }
}

Browser support: IE8+ and everything else.

Installation

npm install xhr

Example

var xhr = require("xhr")

xhr({
    method: "post",
    body: someJSONString,
    uri: "/foo",
    headers: {
        "Content-Type": "application/json"
    }
}, function (err, resp, body) {
    // check resp.statusCode
})

var req = xhr(options, callback)

type XhrOptions = String | {
    useXDR: Boolean?,
    sync: Boolean?,
    uri: String,
    url: String,
    method: String?,
    timeout: Number?,
    headers: Object?,
    body: String? | Object?,
    json: Boolean? | Object?,
    username: String?,
    password: String?,
    withCredentials: Boolean?,
    responseType: String?,
    beforeSend: Function?
}
xhr := (XhrOptions, Callback<Response>) => Request

the returned object is either an XMLHttpRequest instance or an XDomainRequest instance (if on IE8/IE9 && options.useXDR is set to true)

Your callback will be called once with the arguments ( Error, response , body ) where the response is an object:

{
    body: Object||String,
    statusCode: Number,
    method: String,
    headers: {},
    url: String,
    rawRequest: xhr
}

Your callback will be called with an Error if there is an error in the browser that prevents sending the request. A HTTP 500 response is not going to cause an error to be returned.

Other signatures

  • var req = xhr(url, callback) - a simple string instead of the options. In this case, a GET request will be made to that url.

  • var req = xhr(url, options, callback) - the above may also be called with the standard set of options.

Convience methods

  • var req = xhr.{post, put, patch, del, head, get}(url, callback)
  • var req = xhr.{post, put, patch, del, head, get}(options, callback)
  • var req = xhr.{post, put, patch, del, head, get}(url, options, callback)

The xhr module has convience functions attached that will make requests with the given method. Each function is named after its method, with the exception of DELETE which is called xhr.del for compatibility.

The method shorthands may be combined with the url-first form of xhr for succinct and descriptive requests. For example,

xhr.post('/post-to-me', function(err, resp) {
  console.log(resp.body)
})

or

xhr.del('/delete-me', { headers: { my: 'auth' } }, function (err, resp) {
  console.log(resp.statusCode);
})

Options

options.method

Specify the method the XMLHttpRequest should be opened with. Passed to XMLHttpRequest.open. Defaults to "GET"

options.useXDR

Specify whether this is a cross origin (CORS) request for IE<10. Switches IE to use XDomainRequest instead of XMLHttpRequest. Ignored in other browsers.

Note that headers cannot be set on an XDomainRequest instance.

options.sync

Specify whether this is a synchrounous request. Note that when this is true the callback will be called synchronously. In most cases this option should not be used. Only use if you know what you are doing!

options.body

Pass in body to be send across the XMLHttpRequest. Generally should be a string. But anything that's valid as a parameter to XMLHttpRequest.send should work (Buffer for file, etc.).

If options.json is true, then this must be a JSON-serializable object. options.body is passed to JSON.stringify and sent.

options.uri or options.url

The uri to send a request to. Passed to XMLHttpRequest.open. options.url and options.uri are aliases for each other.

options.headers

An object of headers that should be set on the request. The key, value pair is passed to XMLHttpRequest.setRequestHeader

options.timeout

Number of miliseconds to wait for response. Defaults to 0 (no timeout). Ignored when options.sync is true.

options.json

Set to true to send request as application/json (see options.body) and parse response from JSON.

For backwards compatibility options.json can also be a valid JSON-serializable value to be sent to the server. Additionally the response body is still parsed as JSON

For sending booleans as JSON body see FAQ

options.withCredentials

Specify whether user credentials are to be included in a cross-origin request. Sets XMLHttpRequest.withCredentials. Defaults to false.

A wildcard * cannot be used in the Access-Control-Allow-Origin header when withCredentials is true. The header needs to specify your origin explicitly or browser will abort the request.

options.responseType

Determines the data type of the response. Sets XMLHttpRequest.responseType. For example, a responseType of document will return a parsed Document object as the response.body for an XML resource.

options.beforeSend

A function being called right before the send method of the XMLHttpRequest or XDomainRequest instance is called. The XMLHttpRequest or XDomainRequest instance is passed as an argument.

options.xhr

Pass an XMLHttpRequest object (or something that acts like one) to use instead of constructing a new one using the XMLHttpRequest or XDomainRequest constructors. Useful for testing.

FAQ

  • Why is my server's JSON response not parsed? I returned the right content-type.
    • See options.json - you can set it to true on a GET request to tell xhr to parse the response body.
    • Without options.json body is returned as-is (a string or when responseType is set and the browser supports it - a result of parsing JSON or XML)
  • How do I send an object or array as POST body?
    • options.body should be a string. You need to serialize your object before passing to xhr for sending.
    • To serialize to JSON you can use options.json:true with options.body for convenience - then xhr will do the serialization and set content-type accordingly.
  • Where's stream API? .pipe() etc.
    • Not implemented. You can't reasonably have that in the browser.
  • Why can't I send "true" as body by passing it as options.json anymore?
    • Accepting true as a value was a bug. Despite what JSON.stringify does, the string "true" is not valid JSON. If you're sending booleans as JSON, please consider wrapping them in an object or array to save yourself from more trouble in the future. To bring back the old behavior, hardcode options.json to true and set options.body to your boolean value.
  • How do I add an onprogress listener?
    • use beforeSend function for non-standard things that are browser specific. In this case:
    xhr({
      ...
      beforeSend: function(xhrObject){
        xhrObject.onprogress = function(){}
      }
    })

Mocking Requests

You can override the constructor used to create new requests for testing. When you're making a new request:

xhr({ xhr: new MockXMLHttpRequest() })

or you can override the constructors used to create requests at the module level:

xhr.XMLHttpRequest = MockXMLHttpRequest
xhr.XDomainRequest = MockXDomainRequest

MIT Licenced

xhr's People

Contributors

artskydj avatar audionerd avatar bevacqua avatar bjoerge avatar chesles avatar conradz avatar djake avatar dominykas avatar fliptheweb avatar gitter-badger avatar greenkeeper[bot] avatar hughsk avatar iwillig avatar jamesplease avatar kemitchell avatar kesla avatar max-mapper avatar mhart avatar mscdex avatar nadavspi avatar naugtur avatar ossdev07 avatar raynos avatar scottcorgan avatar stuartsan avatar tehshrike avatar tomap avatar victorfranz avatar wbinnssmith avatar zunsthy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xhr's Issues

how to make cross-site request?

hi, i want to make a cors request and my code is like:

var req = httpify({
url: 'remoteurl address',
method: 'GET',
cors: true,
}, function (err, response, body) {
console.log(response);
});

but the request can not send with the cookies so i will get 401 response.

which part did i go wrong?

Accept header in incoming options is checked case sensitive

Solution

(headers["accept"] || headers["Accept"]) || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user

Is this still readable?

Or should we loop over all headers and lowercase them (thus overwriting duplicates) ?

ECONNRESET err.code for abort()

Currently you can abort requests like so:

var req = xhr(url, (err, resp) => {
   // ...
})

// when user cancels request
cancelButton.on('click', () => req.abort())

However, it just passes err as "Internal XMLHttpRequest Error" which makes it hard to distinguish between user-initiated cancellation and actual XHR errors.

If possible, it would be nice to emulate Node and use ECONNRESET for err.code when the error is due to an abort() rather than, say, a connection or JSON parse error. This way abstractions like xhr-request that work in Node/Browser could both use the same logic for handling req.abort().

Update global

Hi,

Related to my issue here: AmpersandJS/ampersand#47
Could you update [email protected] to the latest version (4.2.1)?

The version you are using (2.0.7) references [email protected], which references by mistake tape, which is only needed for tests purposes.
It increases quite a lot browserify's bundles for nothing.
Newer versions have corrected this.

Thanks!

Way to know timeout errors vs other errors?

It looks like all errors call the same error function but we need to know specifically when it's a timeout error to inform an adaptive video algorithm. Am I missing anything, or any suggestions on how it should be added?

Thanks.

Use a service for running tests in browsers

[edited]

The idea is to find and configure a service for running tests in multiple browsers to monitor browser support automatically.

The trick is, we need the server side for the tests running in the same origin.

See how npm test does it now.

Add option (or default) to parse option.json and add as URL arguments

For GET (and possibly PUT & DELETE), options.json is not automatically added to the URL as parameter arguments. i.e.

xhr({url:'/service', json:{arg1:'foo', arg2:'bar'},function(err, resp, body){ })

/service?arg1=foo&arg2=bar

Most other libraries will do this so it would be great if this npm would also do this by defualt OR at least process as an options.

Version bump?

I was using xhr@latest from npm and just got hit by the InvalidStateError fixed in #8

Consider moving XHR/XDR aliases inside createXHR()

Having these two lines outside of the main exporter function means, that the aliases are created before the module is required:

var XHR = window.XMLHttpRequest || noop
var XDR = "withCredentials" in (new XHR()) ?
        window.XMLHttpRequest : window.XDomainRequest

And since the aliases are already created, it is impossible to stub/mock XMLHttpRequest with e.g. http://sinonjs.org/docs/#server Considering that it is also impossible to fake out the exported function itself, it becomes practically impossible to properly unit test anything that uses the xhr module without having an actual HTTP server, which is not always feasible.

If the above two lines were inside createXHR() - we could hijack/fake XMLHttpRequest before calling the exported function. Aside from negligible performance impact, I don't really see any good reason not to do so - does one exist?

isomorphism

would you add node-XMLHttpRequest as a dependency if we marked it as 'false' in the browser field?

would make xhr nicely isomorphic w/o any overhead in the browser bundle

crash in Firefox when sync == true

when sync == true xhr will crash with the following error:

InvalidAccessError: A parameter or an operation is not supported by the underlying object

On the line

xhr.withCredentials = !!options.withCredentials

On the related MDN page it is said that

Note: Starting with Gecko 11.0 (Firefox 11.0 / Thunderbird 11.0 / SeaMonkey 2.8), Gecko no
longer lets you use the withCredentials attribute when performing synchronous requests.
Attempting to do so throws an NS_ERROR_DOM_INVALID_ACCESS_ERR exception.

Convenience APIs

Hey! Love this module — it's awesome. I'm wondering if you're open to one or both of the following convenience APIs in the same vein as request:

  • xhr.post, xhr.put, etc. as shortcuts for their respective methods.
  • Providing a string as the first parameter as the url for the request, with options as they are provided as the second parameter.

This would enable requests like this:

xhr.post("http://example.com", function (err, resp) {
  // ...
})

These would allow for more expressive xhr calls, though they'd introduce more than one way to do things and could slightly complicate this simple library with a magical API. IMHO this would be worth the tradeoff, but I thought I'd reach out before creating a PR or a wrapper library.

Thanks again.

Add warning where request/request compatible code would silently fail

request has some options that would be silently ignored if one tried to use them with xhr, eg.

requestORxhr({
  url: "http://google.com",
  qs: {
    q: "how do I search internet?"
  }
})

request will send the query, but xhr doesn't support qs (if it should is a totally different matter, we keep the size small)

Proposal:
run console.warn(optionKey+' option not implemented')

Browserify fails

I'm using Ampersand.js, which uses this module.

When running Browserify, I get the following error:

>> SyntaxError: Line 78: Unexpected token throw while parsing /var/www/onthego/client/node_modules/ampersand-model/node_modules/ampersand-sync/node_modules/xhr/index.js

Changing the options.headers... line back to a console warning fixes it (see this commit).

Drop IE8 support

I know, I know, it was just added :) But still - I'm raising this issue to at least get an official "no" - or maybe someone has a more concrete idea on how to decouple IE8 support / inject the header parsing function?

IE8 support depends on parse-headers, which is an extra 1.5kb browserified+minified. I'm using xhr itself exactly because it is small - ~2kb browserified+minified, so IE8 support effectively doubles the size. Some might say that's not much, but over here we have to save every byte (building a script for 3rd party sites...). Plus IE8 is not a Real Browser™ anyways.

Where did the 2.0.2 hide?

I published a new version recently, but I couldn't publish 2.0.2 because it was already taken. There is no tag for 2.0.2. I can't find it, so I published 2.0.3 and all's well. But I feel like we should tag the 2.0.2 on the right commit or unpublish 2.0.2
I couldn't find the commit that was published as 2.0.2. That's the magic of DVCS ;)

Easy access to cookies?

Is there an easy helper to get access to the cookie information, or do we have to parse the header ourselves? If not, would you accept a patch that parsed the cookie information and accepted a key, returning the cookie's value?

We lost IE8/9 support

I checked on virtual machines at work, and we lost support for old IEs
It seems that the only problem for now is that parse-headers is using .indexOf
Good news: noone reported that ;)

  1. We need to re-run tests in IEs more often. Who can help? Who has experience with browserstack free for opensource option?
  2. Change parse-headers or replace with something else?
  3. Integration tests are not IE compatible, I'll fix them by the end of the week (my bad)

testling webhook doesn't seem to work

The latest testling run is dated 2014-07-22 and we had quite a lot of pushes since then.
Also, the browser support banner is invalid because of that.

Why 2.0?

What change to xhr caused the major version bump?

options.json in GET, HEAD, accept application/json?

If options.json I think it makes sense to set headers: { Accept: 'application/json' }.

e.g

GET:

var options = {
  url: url,
  json: true,
  headers: { Accept: 'application/json' }
};
xhr(options, handle);

POST:

var options = {
  method: 'POST',
  url: url,
  json: true
};
xhr(options, handle);

Seems inconsistent to me

Object doesn't support this action - IE 8

This is the line generating the error:

xhr.status = xhr.statusCode = status

My code is super simple:

module.exports = function sendDataToServer(data, cb) {
  xhr({
    json: data,
    uri: "/url",
    method: "POST"
  }, function (err, resp, body) {
    cb(err, resp, body);
    // resp === xhr
    // check resp.body or resp.statusCode
  })
};

Incorrect error message created in 1.17

When a request is sent with a body and results in a http error response, the error message is created based on the response body, but the body is not passed to the error creating function and the request body is accidentally used.

seems introduced in 1.15

GET request url params

Is there any way to have GET request url parameters without appending them to the URL yourself beforehand?

Could not find any documentation on this.

CORS + `json` == blow up

var xhr = require('xhr');

module.exports = function (done) {
  var opts = {
    url: 'http://api.icndb.com/jokes/random', crossOrigin: true, json: null,
    //headers: Object.freeze({})
  };

  httpify(opts, cb);

  function cb (err, res, body) {
    console.log(err, body);
    done(err, JSON.parse(body).value.joke);
  }
}

module.exports(function(){});

Yields

OPTIONS http://api.icndb.com/jokes/random Request header field Content-Type is not allowed by Access-Control-Allow-Headers. bundle.js:102
XMLHttpRequest cannot load http://api.icndb.com/jokes/random. Request header field Content-Type is not allowed by Access-Control-Allow-Headers. quote.html:1
Error {statusCode: 0} null

Uncommenting the headers: Object.freeze({}) line fixes the issue. This is obviously an awful hack, maybe we shouldn't touch Content-Type when CORS

npm module `request` compatible HTTP error handling

xhr is a good replacement for request module in node, the options object is compatible and the callback function signature as well. The only important difference is that request passes the error variable not null only when an actual error happens in it. If http call ends in non-2xx response, error is still null.

It would break the backwawrd-compatibility, but we could consider switching to matching logic, so that xhr can be used two-platform projects as a straightforward replacement of the request module.

thoughts?

support for `form` option?

The request library has a helpful form option, which is the equivalent of doing:

request({
    form: body
})
// is equivalent to...
request({
    body: querystring.stringify(body).toString('utf8'),
    headers: {"Content-Type": "application/x-www-form-urlencoded"}
})

The option handles the body stringification & header-setting for me, which I find much easier to work with. Is there any interest to add a similar option to xhr? It would make moving from request to nets much easier for myself and a lot of others.

Happy to submit a PR if so.

Separate cors from allowCredentials

Currently if I set options.cors to true to get XDR to be used in IE I have to use beforeSend to turn allowCredentials back off. It should be a separate setting IMHO.

Setting options.cors has no effect on non-IE8/9 browsers except it can cause the error:

XMLHttpRequest cannot load https://qqqq A wildcard '*' cannot be used in the 
'Access-Control-Allow-Origin' header when the credentials flag is true. 
Origin 'https://aaaa' is therefore not allowed access.

Sending FormData

Is it possible to send FormData as the body? The docs claim "anything that's valid as a parameter to xhr.send should work", but my requests seem to fail (a server error about boundary not being defined). I defined the Content-Type headers as multipart/form-data, but this doesn't do the trick.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.