From 88e7cf5454015d88ef0761d49d965a77f7add1aa Mon Sep 17 00:00:00 2001 From: Patrik Johnson Date: Thu, 4 Aug 2016 19:19:12 +0300 Subject: [PATCH] Rewrite: ignore m.request deserialize option if extract is passed --- docs/request.md | 22 +++++++++++++++++++++- docs/v1.x-migration.md | 2 ++ request/request.js | 2 +- request/tests/test-xhr.js | 21 +++++++++++++++++++-- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/request.md b/docs/request.md index 4fd9a1cf..6ab13da3 100644 --- a/docs/request.md +++ b/docs/request.md @@ -32,7 +32,7 @@ Argument | Type | Required | Descript `options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function). `options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`, or if `options.data` is an instance of [`FormData`](https://developer.mozilla.org/en/docs/Web/API/FormData), defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function) (i.e. `function(value) {return value}`). `options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the response. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses. -`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText` +`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`. If defined, `options.deserialize` is ignored. `options.initialValue` | `any` | No | A value to populate the returned stream before the request completes `options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods. **returns** | `Stream` | | A stream that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods @@ -378,6 +378,26 @@ Ignoring the fact that the parseCSV function above doesn't handle a lot of cases --- + +### Retrieving response details + +By default Mithril attempts to parse a response as JSON and returns `xhr.responseText`. It may be useful to inspect a server response in more detail, this can be accomplished by passing a custom `options.extract` function: + +```javascript +m.request({ + method: "GET", + url: "/api/v1/users", + extract: function(xhr) {return {status: xhr.status, body: xhr.responseText}} +}) +.run(function(response) { + console.log(response.status, response.body) +}) +``` + +The parameter to `options.extract` is the XMLHttpRequest object once its operation is completed, but before it has been passed to the resulting [stream](prop.md), so the stream may still end up in an errored state if processing throws an exception. + +--- + ### Why JSON instead of HTML Many server-side frameworks provide a view engine that interpolates database data into a template before serving HTML (on page load or via AJAX) and then employ jQuery to handle user interactions. diff --git a/docs/v1.x-migration.md b/docs/v1.x-migration.md index 20cfbeb6..b9f5e8d1 100644 --- a/docs/v1.x-migration.md +++ b/docs/v1.x-migration.md @@ -330,3 +330,5 @@ m.prop.merge([ console.log("Contributors:", users[0].name, "and", users[1].name); }); ``` + +Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be passed to the [m.prop stream](prop.md) directly, and any `deserialize` callback is ignored. diff --git a/request/request.js b/request/request.js index 01102d3b..7447bb7e 100644 --- a/request/request.js +++ b/request/request.js @@ -38,7 +38,7 @@ module.exports = function($window) { xhr.onreadystatechange = function() { if (xhr.readyState === 4) { try { - var response = args.deserialize(args.extract(xhr, args)) + var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args)) if (xhr.status >= 200 && xhr.status < 300) { stream(cast(args.type, response)) } diff --git a/request/tests/test-xhr.js b/request/tests/test-xhr.js index bfbd1dca..3dc98902 100644 --- a/request/tests/test-xhr.js +++ b/request/tests/test-xhr.js @@ -210,7 +210,7 @@ o.spec("xhr", function() { } }) xhr({method: "GET", url: "/item", extract: extract}).map(function(data) { - o(data).deepEquals({test: 123}) + o(data).equals("{\"test\":123}") }).map(done) }) o("extract parameter works in POST", function(done) { @@ -224,7 +224,24 @@ o.spec("xhr", function() { } }) xhr({method: "POST", url: "/item", extract: extract}).map(function(data) { - o(data).deepEquals({test: 123}) + o(data).equals("{\"test\":123}") + }).map(done) + }) + o("ignores deserialize if extract is defined", function(done) { + var extract = function(data) { + return data.status + } + var deserialize = o.spy() + + mock.$defineRoutes({ + "GET /item": function(request) { + return {status: 200, responseText: ""} + } + }) + xhr({method: "GET", url: "/item", extract: extract, deserialize: deserialize}).map(function(data) { + o(data).equals(200) + }).map(function() { + o(deserialize.callCount).equals(0) }).map(done) }) o("config parameter works", function(done) {