How do I get the status code back from a failed ajax call in RxJS so I can decide what to do with it?
import { ajax } from 'rxjs/observable/dom/ajax'
ajax('https://my.url')
.map(xhr => console.log('woo', xhr.response))
.catch(err => console.log('status code??'))
there are some fields on the err response and one of them is status but it's always 0 irrespective of the statusCode.
Edit:
I missed the fact that you see error.status so the question is just that why it's zero.
It's a browser thing. It's zero by default, and only gets changed when the request actually comes back. If it does not for any reason complete, it remains zero. That includes aborted requests, CORS issues, being offline, DNS issues, and any other network error. Which makes sense, cause there are no HTTP codes for most of these cases. A CORS request error might itself have a 401 (or other code) but the browser does not expose it to you programmatically.
Unfortunately, when this happens there's not much you can do programmatically to know what caused it. You can check navigator.onLine and if it's false might infer that it was caused by not being connected to the internet, though that's not 100% reliable.
In other cases, you're screwed, programmatically. There's no error message property with an explanation or other way to know. The true reason the error is typically in the dev console (so check there), but not accessible programmatically for security reasons.
Here are some additional resources about this:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status
https://stackoverflow.com/a/26451773/1770633
XMLHttpRequest status 0 (responseText is empty)
In v5 (and at least in v4 too), status is available as a top-level property status of the provided error object:
import { ajax } from 'rxjs/observable/dom/ajax'
ajax('https://my.url')
.map(xhr => console.log('woo', xhr.response))
.catch(err => {
console.log('status code', error.status);
// also available as error.xhr.status (inside the raw XMLHttpRequest object)
return Observable.empty(); // switch to an empty stream i.e. swallow error
});
Note that catch is used to catch an error and then switch to a different Observable that you must return. So the error must be handled. If you do not want to handle the error, but just want to log it, you can use do:
ajax('https://my.url')
.map(xhr => console.log('woo', xhr.response))
.do({ error: err => console.log('status code', err.status) })
As per documentation: https://github.com/Reactive-Extensions/RxJS-DOM/blob/master/doc/operators/ajax.md
You do it like below:
ajax('https://my.url')
.map(xhr => console.log('woo', xhr.response))
.catch((err, status) => console.log(status))
Related
I'm trying to assert on the request body to ensure the correct new test card is passed as part of the order.
it("User clicks confirm & pay button to complete order", () => {
cy.intercept("/api/checkout/payandcommit").as("placeOrder");
cy.placeOrderAndPay();
cy.wait("#placeOrder")
.its("response.statusCode")
.should("eq", 200)
.its("request.body")
.should("include", "cardNumber", 370000000000002);
});
All is good upto verfying the status code then it breaks.
This is the error thrown:
Timed out retrying: cy.its() errored because the property: request does not exist on your subject.
cy.its() waited for the specified property request to exist, but it never did.
If you do not expect the property request to exist, then add an assertion such as:
cy.wrap({ foo: 'bar' }).its('quux').should('not.exist')
If i comment out the status code assertion this new error is thrown: object tested must be an array, a map, an object, a set, a string, or a weakset, but object given.
Any help getting this working would be much appreciated!
Chaining the assertions like this doesn't work, because the subject changes inside the chain
cy.wait("#placeOrder") // yields interception object
.its("response.statusCode") // yields number 200
.should("eq", 200) // yields it's input (number 200)
.its("request.body") // not testing the interception object here
.should("include", "cardNumber", 370000000000002);
One way that works is to use a callback which gets the interception object
cy.wait('#placeOrder').then(interception => {
console.log(interception); // take a look at the properties
cy.wrap(interception.response.statusCode).should('eq', 404);
cy.wrap(interception.request.body)
.should("include", "cardNumber", 370000000000002) // not sure this should is correct
.should("have.property", "cardNumber", 370000000000002) // maybe this is better
})
You may also be able to use chained commands if the subject is maintained, which means you have to tweak the should() in the middle
cy.wait("#placeOrder")
.should('have.property', 'response.statusCode', 200)
.should('have.property', 'request.body.cardNumber', 370000000000002);
Check out the logged interception object to make sure you have the correct properties and property value types (e.g is cardNumber a number or a string?).
Thank you so much for your help Hiram K! I was able to get it working with:
cy.wait("#placeOrder").then((interception) => {
console.log(interception);
cy.wrap(interception.response.statusCode).should("eq", 200);
cy.wrap(interception.request.body.paymentDetails[0].cardNumber).should(
"include",
"370000000000002"
);
cy.wrap(
interception.request.body.paymentDetails[0].defaultPayment
).should("eq", false);
});
I am using admin-on-rest (1.3.2) and trying to skip the default behaviour of calling AUTH_LOGOUT action on Promise rejection. I want to drop my behavior.
I found an issue on their Github Issues:
https://github.com/marmelab/admin-on-rest/issues/894, but without much information about the implementation here.
In the browser console, I see that executed saga is here:
https://github.com/marmelab/admin-on-rest/blob/v1.3.2/src/sideEffect/saga/crudResponse.js#L92-L97
I've just realized why what AUTH_LOGOUT happening.
My AUTH_ERROR check:
if (type === AUTH_ERROR) {
const { status } = params
if (status === 401) {
localStorage.removeItem('admin')
return Promise.reject()
}
}
Does not have check for other status codes rather than 401 and there was no Promise.resolve returned, which leads to Promise.reject('Unknown method') returned.
I'm new to promises, but as I understand it, .catch usually belongs at the end of a chain of promises:
promiseFunc()
.then( ... )
.then( ... )
.catch( // catch any errors along the chain )
What if the promises are split in between functions? Do I catch at the end of every function?
func1 () {
promiseFunc1()
.then((result) => {
promiseFunc2()
)
// should I .catch here?
}
func2 () {
func1()
.then((result) => {
// do stuff
})
.catch(console.log.bind(console)); // this also catches errors from func1
}
Maybe this is a symptom of another error (in which I'd love to hear if I'm doing this wrong), but when I try catching at the end of func1, I end up reaching the .then block of func2, with result = undefined. After deleting the catch in func1, it works -- but it feels wrong that func1 should expect any functions calling it to catch its errors.
.catch() works very much like try/catch. You should .catch() wherever you want or need to handle the error and either log something or change the course of the chain.
If all you want is for the promise chain to abort when an error occurs, then you can just put one .catch() at the very end of the chain and deal with the error there.
If, on the other hand, you have some sub-part of the chain that, if it has a rejection you want to do something different and allow the chain to continue or to take a different path, then you need to .catch() at that level where you want to influence things if there's an error.
All or Nothing Catch at the End
So, let's say you have four functions that all return promises.
If you do this:
a().then(b).then(c).then(d).then(finalResult => {
// final result here
}).catch(err => {
// deal with error here
});
Then, if anyone of your four promises rejects, then the rest of the chain will abort and it will skip to your one .catch(). For some operations, this is the desired behavior. If you intend to fetch some data from an external server, use that data to then fetch some other data from another external server and then write that data to disk, the whole process is pretty much all or nothing. If any of the earlier steps fails, there's nothing else you can do as the whole operation has failed, you may as well just use one .catch() at the end and report that error.
Intervening Catch to Change the Behavior Mid-Chain upon Error
On, the other hand suppose you have a situation were you want to fetch some data from an external server, but if that server is down, then you want to fetch the data from an alternate server. In that case, you want to catch the error from the first fetch and try something else. So, you'd use a .catch() on the very first step and attempt something different:
fetch1().catch(fetch2).then(b).then(c).then(finalResult => {
// final result here
}).catch(err => {
// deal with error here
});
Logging and Rethrow
When building sub-systems that others will use, it is often useful to log certain types of errors. So, even the whole promise chain might be all or nothing, you still may want to log an error earlier in the chain and then rethrow the error so that the chain continues in the rejected state:
function someFuncOthersUse() {
return a().then(b).then(c).catch(err => {
// you want your own logging any time a or b or c rejects
console.log("Error on fetchB", err);
throw err;
});
}
The caller will then be doing:
someFuncOthersUse().then(finalResult => {
// final result here
}).catch(err => {
// deal with error here
});
I am new to SignalR and there is a small detail I can't get my head around.
My SignalR hub include many channels and the clients can join one or many of these channels via a server method:
joinChannel(string channelName)
What I don't understand is what this method should return.
If it were a normal "RPC" method I would return a status (200 - Ok, 404 - Not found, 403 - Forbidden etc) via IHttpActionResult.
How do I indicate success/failure in SignalR?
What determines if the reply gets to .done or .fail in the client?
Update
Currently my method returns a non-zero value in case of error.
int joinChannel(string channelName) {
...
return errorCode;
}
This works but it create unnecessarily complicated code in the client
hubProxy.server.joinChannel('channel1')
.done(function (result) {
if (result != 0) {
// error handling
}
})
.fail(function (error) {
// error handling
});
To confirm that your action was successfully performed, you can have a client method call. So, basically it would look like this:
public void ServerMethod(argumentList)
{
if (/* server code executed successfully */)
Clients.Caller.onSuccess(arguments);
else Clients.Caller.onFailure(arguments);
}
What this piece of code does is to notify the caller of the server method of a success/failure by calling a client method - method defined in JavaScript. You can also have a method executed on All clients, or only on specific users.
Since it is not an RPC mechanism, I think this is the closest thing you can do to simulate a return type in SignalR.
Hope this helps!
Best of luck!
What about
HubException in
Microsoft.AspNetCore.SignalR.
This is available in ASP.NET Core.
This exception is thrown on the server and sent to client. You can also derive from that class to put your own information in it.
I'm not sure if I'm understanding a certain aspect of promises correctly and I couldn't find what I was looking for after a brief google/SO search.
Can a resolved promise returned to a rejected callback ever fire a resolved method later in the chain? Using jQuery deferreds (and it's deprecated pipe method) as an example:
x = $.Deferred();
x
.then(null,function() {
return $.Deferred().resolve();
})
.then(function() {
console.log('resolved');
},function() {
console.log('rejected');
});
The above code, logs rejected, I would've expected it to log resolved because the Deferred in the first error callback returns a resolved promise.
On the contrary, the same code using jQuery's deprecated pipe method logs, as I would've expected resolved.
x = $.Deferred();
x
.pipe(null,function() {
return $.Deferred().resolve();
})
.pipe(function() {
console.log('resolved');
},function() {
console.log('rejected');
});
Am I thinking about something wrong by trying to resolve a promise inside a rejected callback?
For anybody who has run into this same thought process, the following page helped me out: https://gist.github.com/domenic/3889970
Specifically:
...you can feed the return value of one function straight into another,
and keep doing this indefinitely. More importantly, if at any point
that process fails, one function in the composition chain can throw an
exception, which then bypasses all further compositional layers until
it comes into the hands of someone who can handle it with a catch.
It seems jQuery's pipe method was a violation of the Promise's specification.