HTTP Status 0 from AngularJS Get for JSON - json

I'm running a $http.get for a JSON and am getting a status of 0. I've downloaded the same JSON and the get works locally, and in Python using the requests library I can get the JSON no problem, but in AngularJS it's not working. What I don't understand is why angular isn't getting it but everything else is. Code snippet below.
function AgentListCtrl($scope, $http) {
$http.get('http://foo.bar/api/objects').success(function(data) {
$scope.objects = data;
}).error(function(data, status) {
$scope.status1 = status;
});
This provides the JSON and parses it when using a local file, but otherwise it fails and sets status1 to 0.

Just to make this clear since is not directly stated in the above answer (but in its comments) and, like me, some Angular newbies may be spending some time on this:
Angular's $resource will be able to execute a REST verb on another server, which in turn will respond correctly (with a status 200). Angular will nevertheless fail with a cryptical message, identifyiable by the status 0. It is further misleading since, in a browser's debugger, you may actually see the server's answer.
Angular will do an OPTIONS request on a cross-domain request (at least for the default query() method) unless specified on the contrary. Usually the server will not answer with the desired content (i.e. your representation). One simple way of doing this per request is specifying the method to be 'GET'.
$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
The server answering your REST requests MUST include the headers specified by CORS [1] in order to allow Angular to consume properly the response. Essentially this means including the Access-Control-Allow-Origin header in your response, specifying the servers from where the request comes from, that are allowed. This value may be *.
Complementing this answer for anyone integrating AngularJS with spring-data-rest-webmvc:
the HATEOAS json formatted response will not be properly consumed by Angular, producing instead the error Expected response to contain an array but got an object. This is solved by adding the isArray: false parameter to the $resouce's configuration;
a very to-the-point example of configuring CORS for the spring-data-rest-webmvc scenario is presented at [2] (see the SimpleCORSFilter)
[1] https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
[2] https://spring.io/guides/gs/rest-service-cors/

In your code, the status assignment only occurs when the error happens. You should be able to get the status when the call was made successfully like this:
success(function(data, status, headers, config) {
$scope.objects = data;
$scope.status1 = status;
}).error(function(data, status) {
$scope.status1 = status;
});

I was having a similar problem myself. A third party API that returns JSON just fine through every other means was failing with status 0 when called through Angular's $http.get() method.
In my case there wasn't any CORS problem. Instead, the URL I was using for the API was not quite right and the server was issuing a 301 response. Angular wasn't respecting the redirect.
Word to the wise.

Related

Calling an API HttpGet Action from Angular with a large amount of search criteria throws a CORS policy error

I have a Core 2.2 Web API project as my back-end for an Angular front-end. On one screen I allow the user to select x amount of records for processing from a data grid. Before the records can be processed I need to check the database to see if records exist in another table by passing a list of 3 fields (intelligent key) to my API. I put this list into an object array, do a Json.stringify on that object and send it to my API as a Get request. This works fine as long as I select 1-3 records. As soon as I select 4 or more records I get "Access to XMLHttpRequest at 'request url' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
My Cors policy indicates it should let anything through and I'm also confused by why 1-3 records works fine.
In my startup.cs -> ConfigureServices method I have the Cors policy defined like so:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
In my Angular service I make this call which serializes by array (apiUrl is my url to call my API: https://localhost/api/controller)
getRecordsByIntelligentKey(intelligentKey: IntelligentKey[]): Observable<Record[]>{
const jsonObject = JSON.stringify(intelligentKey);
const url = `${apiUrl}/${jsonObject}`;
return this.http.get<Record[]>(url).pipe(
tap(_ => console.log('fetching records based on intelligent key')),
catchError(this.handleError('getRecordsByIntelligentKey', []))
);
}
In my controller GET action I deserialize my string. I mean I'd like to pass an object but I think I need to do a POST for that.
[HttpGet("{jsonObject}")]
public ActionResult<List<Commission>> GetByCustomerAndTerritoryManager(string jsonObject)
{
try
{
var intelligentKey = JsonConvert
.DeserializeObject<List<ThreeFields>>(jsonObject);
return _repo.GetRecordsByIntelligentKey(intelligentKey);
}
catch (Exception ex)
{
_logger.Error(ex, "Error retrieving records.");
throw;
}
}
Now my problem is the user could select 1000s of records. When I select over 1000 records I just get ERR_CONNECTION_RESET probably due to the querystring being way too long.
I'm thinking I need an object but everything I've researched seems to advise against doing that with a GET and using the POST request instead. Problem is, it's a restful API and I'm already using the POST request for the processing portion. I guess I could use PUT or DELETE but it just feels wrong. I'm going to wire up the PUT right after I post this question to see if it will work but ultimately I'd like to find the correct solution for this.
UPDATE: The PUT method works fine even with over 1000 records selected so I guess this will be my interim solution for now. I still feel like there's code smell and would love to use a GET but at least this allows me to proceed.

Chrome dev tools fails to show response even the content returned has header Content-Type:text/html; charset=UTF-8

Why does my Chrome developer tools show
Failed to show response data
in response when the content returned is of type text/html?
What is the alternative to see the returned response in developer tools?
I think this only happens when you have 'Preserve log' checked and you are trying to view the response data of a previous request after you have navigated away.
For example, I viewed the Response to loading this Stack Overflow question. You can see it.
The second time, I reloaded this page but didn't look at the Headers or Response. I navigated to a different website. Now when I look at the response, it shows 'Failed to load response data'.
This is a known issue, that's been around for a while, and debated a lot.
As described by Gideon, this is a known issue with Chrome that has been open for more than 5 years with no apparent interest in fixing it.
Unfortunately, in my case, the window.onunload = function() { debugger; } workaround didn't work either. So far the best workaround I've found is to use Firefox, which does display response data even after a navigation. The Firefox devtools also have a lot of nice features missing in Chrome, such as syntax highlighting the response data if it is html and automatically parsing it if it is JSON.
For the ones who are getting the error while requesting JSON data:
If your are requesting JSON data, the JSON might be too large and that what cause the error to happen.
My solution is to copy the request link to new tab (get request from browser)
copy the data to JSON viewer online where you have auto parsing and work on it there.
As described by Gideon, this is a known issue.
For use window.onunload = function() { debugger; } instead.
But you can add a breakpoint in Source tab, then can solve your problem.
like this:
If you make an AJAX request with fetch, the response isn't shown unless it's read with .text(), .json(), etc.
If you just do:
r = fetch("/some-path");
the response won't be shown in dev tools.
It shows up after you run:
r.then(r => r.text())
"Failed to show response data" can also happen if you are doing crossdomain requests and the remote host is not properly handling the CORS headers. Check your js console for errors.
For the once who receive this error while requesting large JSON data it is, as mentioned by Blauhirn, not a solution to just open the request in new tab if you are using authentication headers and suchlike.
Forturnatly chrome does have other options such as Copy -> Copy as curl.
Running this call from the commandoline through cURL will be a exact replicate of the original call.
I added > ~/result.json to the last part of the commando to save the result to a file.
Otherwise it will be outputted to the console.
For those coming here from Google, and for whom the previous answers do not solve the mystery...
If you use XHR to make a server call, but do not return a response, this error will occur.
Example (from Nodejs/React but could equally be js/php):
App.tsx
const handleClickEvent = () => {
fetch('/routeInAppjs?someVar=someValue&nutherVar=summat_else', {
method: 'GET',
mode: 'same-origin',
credentials: 'include',
headers: {
'content-type': 'application/json',
dataType: 'json',
},
}).then((response) => {
console.log(response)
});
}
App.js
app.route('/getAllPublicDatasheets').get(async function (req, res) {
const { someVar, nutherVar } = req.query;
console.log('Ending here without a return...')
});
Console.log will here report:
Failed to show response data
To fix, add the return response to bottom of your route (server-side):
res.json('Adding this below the console.log in App.js route will solve it.');
I had the same problem and none of the answers worked, finally i noticed i had made a huge mistake and had chosen other as you can see
Now this seems like a dumb mistake but the thing is even after removing and reinstalling chrome the problem had remained (settings are not uninstalled by default when removing chrome) and so it took me a while until I found this and choose All again...!
This happened because my backend doesn't handle OPTIONS method and because I had clicked on other by mistake which caused me to spend a couple days trying answers!
As long as the body of the Response is not consumed within your code (using .json() or .text() for instance), it won't be displayed in the preview tab of Chrome dev tools
Bug still active.
This happens when JS becomes the initiator for new page(200), or redirect(301/302)
1 possible way to fix it - it disable JavaScript on request.
I.e. in puppeteer you can use: page.setJavaScriptEnabled(false) while intercepting request(page.on('request'))
another possibility is that the server does not handle the OPTIONS request.
One workaround is to use Postman with same request url, headers and payload.
It will give response for sure.
For me, the issue happens when the returned JSON file is too large.
If you just want to see the response, you can get it with the help of Postman. See the steps below:
Copy the request with all information(including URL, header, token, etc) from chrome debugger through Chrome Developer Tools->Network Tab->find the request->right click on it->Copy->Copy as cURL.
Open postman, import->Rawtext, paste the content. Postman will recreate the same request. Then run the request you should see the JSON response.
[Import cURL in postmain][1]: https://i.stack.imgur.com/dL9Qo.png
If you want to reduce the size of the API response, maybe you can return fewer fields in the response. For mongoose, you can easily do this by providing a field name list when calling the find() method.
For exmaple, convert the method from:
const users = await User.find().lean();
To:
const users = await User.find({}, '_id username email role timecreated').lean();
In my case, there is field called description, which is a large string. After removing it from the field list, the response size is reduced from 6.6 MB to 404 KB.
Use firefox, it always display the response and give the same tools that chrome does.

Detect malformed/invalid JSON in an AngularJS $http.post() call

As can be seen in AngularJS's source, any $http.post request that returns an HTTP code in the 200-299 range will trigger the success() callback even if the response contains invalid data (like for example invalid JSON).
I'm specifically setting my call's responseType: 'json' and even then the success callback is fired when something else comes back. This is especially annoying in the development server where PHP's display_errors setting is turned on. When something goes wrong server-side and PHP outputs an error message the AngularJS app doesn't detect this and continues happily.
Is there a way to prevent this? I mean, to make the AngularJS app fire the error() callback when the response data is invalid JSON?
Thanks
so your PHP server responds with a 200 error code even on an error? Not knowing PHP, this feels like a server configuration problem to me. I'd expect a 500 error with a payload. That being said, there are two things that I can think of offhand.
$http includes transformResponse handlers you can set up to inspect the response for problems.
$http also includes the concept of "interceptors" which allow you to pick up the response payload and do something with it. You could use an interceptor to "reject" the response.
More information on transformResponse and "interceptors" in the $http documentation:
http://docs.angularjs.org/api/ng.$http

Calling .Net web service from jQuery+Ajax

I'm trying to call a homemade vb.net web service using jQuery+Ajax and I'm struggling with the specifics.
Here's a small function exposed as a web method:
<WebMethod()> <ScriptMethod(ResponseFormat:=ResponseFormat.Xml, UseHttpGet:=True)> _
Public Function GetAllVotes() As XmlDocument
Dim theVotes = getVotes()
Dim strResult As String = theVotes.XMLSerialize
Dim doc As XmlDocument = New XmlDocument()
doc.LoadXml(strResult)
Return doc
End Function
After looking the web I've added the ScriptMethod attributes since i was returning XML but feel free to tell me i don't need them if that's the case.
Then, on the client side, this is the code :
function getVotes() {
$.support.cors = true;
$.ajax({
type: "GET",
contentType: "application/json",
url: "http://nhrd635:8008/votingmanager.asmx/GetAllVotes",
data: {},
dataType: "xml text jsonp",
success: function(msg) {
// Hide the fake progress indicator graphic.
// Insert the returned HTML into the <div>.
$('#myPlaceHolder').html(msg);
},
error: function(msg) {
$('#myPlaceHolder').html(msg);
// alert(msg);
}
});
}
I've tried many .. many variations of this code, using post or get, changing the content-type, with or without charset=utf-8. with and without double quotes on data: {}.
i use firebug to trace the output my request. only when i set dataType to jsonp do i ever get a result, but in all instances, the code ends up on the "error" function, even when status give 200 OK. but i know that setting it to jsonp is wrong since that gets my xml treated as actual javascript...
I've read very useful blog entries from a guy on encosia.
(sample: http://encosia.com/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/)
but even following his examples i am unable to get a proper return.
am i doing something wrong that's very obvious? is it the fact that i am returning an xml string rather than a json serialized string?
With more perusing of Stack Overflow and the help of Dave Ward from Encosia, I've managed to solve my problem. I've thought I should post my final solution here, in case that helps someone in the future.
First of all, Web Services were a bad way of doing it, I went with the HttpHandler solution, as suggested by Dave Ward in reply to my original question.
Returning XML was also a poor choice, that I wasn't really aware of. I added a reference to JSon.net to my project and used it to transform my object into a Json string.
I really wanted to stick to ".net only" to transform into a json string, as suggested in Dave's blog post, but somehow I struggled to learn how to instruct .net to automatically transform into Json as in Dave's example, so i took an easy way out with Json.net to "get it working"
Then, in my HttpHandler, I had the response string follow the instructions on this post from StackOverflow:
https://stackoverflow.com/a/3703221/1060133
in my case, it was :
context.Response.Write(String.Format("{0}({1});", context.Request("callback"), jsonVotes))
The jquery call also used the instructions in the above post.
Interesting note, even in a parameter-less call, you have to send empty data like so:
$.getJSON('http://url/httpHandler.ashx?callback=?', {},
function(data) {
alert(data);
}
);
Best of luck...
I think most of your trouble here probably stems from the cross-origin request (even making a request across different ports on the same machine counts). That's why you were able to get a glimmer of it working when you switched to JSONP. Unfortunately, ASMX "ScriptServices" don't support JSONP, so the data your WebMethod returned wouldn't be a valid parameter to the JSONP callback function that jQuery injects.
The best solution, if at all possible, is to get the service running on the same domain as the page that's calling it. There are various solutions to the cross-origin problem, but none of them are as widely compatible/reliable as a simple XHR request to the same domain that the page making the request resides on.
If you can't do that, consider enabling CORS support for the site serving up votingmanager.asmx. That doesn't work in most versions of IE, but will allow cross-origin requests in other browsers. More info on how to do that here: http://encosia.com/using-cors-to-access-asp-net-services-across-domains/
Tangentially, I'd avoid the extra XML serialization layer if possible. If getVotes() returns something like a List, use that as your return type and let ASP.NET automatically serialize the collection as JSON and then jQuery will automatically convert that to a JavaScript array in your success handler. More info about that here: http://encosia.com/asp-net-web-services-mistake-manual-json-serialization/

REST/JSON/MVC Return Values

Not a great title but I'm looking more for some guidance, have searched quite a bit. I'm building a web app with an MVC framework (but I think this is a more generic question). I'm trying to make many views that do a lot of AJAX style calls and say I have a site with users and they can add folders and files to their profile page. So the URL maybe like:
/profile/{id}
I have a Profile controller that returns a view with various information. I'd like files and folders listed on the profile to be dynamic so I want to populate it through AJAX calls. I was thinking I would have a URL like
/listFolders/{userId}
and
/listFiles/{folderId}
Is it reasonable to have these URLs return a JSON object for these two URLs and not even provide an HTML view (since, for the browser, the view will just be the whole profile page)? Also, what should I return for errors, say if the user/folder doesn't exist or the current logged in user doesn't have access the data? Is it reasonable to just set 404 or 403 HTTP error codes or do they need to return some kind of HTML? What if there are multiple reasons for it to fail and I'd like to pass that along? Should I arbitrarily choose HTTP error codes or define integer return codes like 0, 1, 2, etc? Also, should the URL specify that they are JSON, like listFoldersJSON instead of listFolders?
I have used JSON in my previous projects. For errors, we return error codes.
We decided to do so because we were dealing with API clients. So we want to deal with error codes (REST is based on HTTP, so it was appropriate to return error codes).
Since you are writing your own application, you can pretty much choose how you want to send your errors to the view. You can create a error json object and in the view you have to check whether this object is not null.
pretty much a if-else in the view. Else you can return error codes and check for the code before rendering the JSON into whatever view you want to.
I would go with error codes, because that complies with the REST philosophy.
Generally speaking, I handle this situation by throwing a 500 internal server error with a status message. Most client libraries such as jQuery provide built in error handling with a failure callback like:
jQuery.ajax({
success:function(response){
//do some success stuff
},
error:function (xhr, ajaxOptions, thrownError){
//handle error
alert(xhr.responseText);
}
});
It's entirely feasible to return JSON objects as opposed to actual views.
As far as the url, you can use listFolders and listFiles without taking on the JSON. However, I recommend you use lower case urls for the sake of how the server is setup. For instance, I know on Apache that sometimes listFiles would be fine, but listfiles would lead to missing page exception.
With regards to errors: You could setup a header of sorts in your JSON response and use whatever system you'd like. For instance, you could do something like
status_code: 0 //where 0 means successful
status_detail:success!
Where, if the status_code is something other than 0, you'd check the status_detail and know to ignore everything else inside the response.
Also, what should I return for errors, say if the user/folder doesn't exist or the current logged in user doesn't have access the data?
These are basic HTTP Error codes:
401 : Unauthorized
404 : Not found
There's a whole slew of error messages in the HTTP spec:
HTTP Status Code Definitions
Also, should the URL specify that they are JSON, like listFoldersJSON instead of listFolders?
Generally, a good way to handle this is for the client to set the 'accepts' header to something like 'text/json' or 'text/xml' and for the server to parse it out and respond with the correct response. This way you can use the same URL but send back different views of the data (if you ever wanted)