AngularJS resource service with jsonp fails - jsonp

I'm trying to fetch the JSON output of a rest api in AngularJS. Here are the problems I'm facing:
The Rest api url has the port number in it which is being interpolated by AngularJS for a variable. I tried several resolutions for this in vain.
I'm having issues with JSONP method. Rest api isn't hosted on the same domain/server and hence a simple get isn't working.
The parameters to the rest api are slash separated and not like a HTML query string. One of the parameters is an email address and I'm thinking the '#' symbol is causing some problem as well. I wasn't able to fix this either.
My rest api looks something like: http://myserver.com:8888/dosomething/me#mydomain.com/arg2.
Sample code / documentation would be really helpful.

I struggled a lot with this problem, so hopefully this will help someone in the future :)
JSONP expects a function callback, a common mistake is to call a URL that returns JSON and you get a Uncaught SyntaxError: Unexpected token : error. Instead, JSONP should return something like this (don't get hung up on the function name in the example):
angular.callbacks._0({"id":4,"name":"Joe"})
The documentation tells you to pass JSON_CALLBACK on the URL for a reason. That will get replaced with the callback function name to handle the return. Each JSONP request is assigned a callback function, so if you do multiple requests they may be handled by angular.callbacks._1, angular.callbacks._2 and so forth.
With that in mind, your request should be something like this:
var url = 'http://myserver.com:8888/dosomething/me#mydomain.com/arg2';
$http.jsonp(url + '?callback=JSON_CALLBACK')
.then(function (response) {
$scope.mydata = response.data;
...
Then AngularJS will actually request (replacing JSON_CALLBACK):
http://myserver.com:8888/dosomething/me#mydomain.com/arg2?callback=angular.callbacks._0
Some frameworks have support for JSONP, but if your api doesn't do it automatically, you can get the callback name from the querystring to encapsulate the json.
Example is in Node.js:
var request = require('request');
var express = require('express');
var app = express();
app.get('/', function(req, res){
// do something to get the json
var json = '{"id":4,"name":"Joe"}';
res.writeHead(200, {"Content-Type": "application/javascript"});
res.write(req.query.callback + '(' + json + ')');
res.end();
});
app.listen(8888);

The main issue I was facing here was related to CORS. I got the $http to retrieve the JSON data from the server by disabling the web security in Chrome - using the --disable-web-security flag while launching Chrome.

Regarding the 8888 port, see if this works:
$scope.url = 'http://myserver.com:port/dosomething/:email/:arg2';
$scope.data = $resource($scope.url, {port:":8888", email:'me#mydomain.com',
arg2: '...', other defaults here}, …)

Try escaping the ':'
var url = 'http://myserver.com\:8888/dosomething/me#mydomain.com/arg2';
Pretty sure I read about this somewhere else

Related

Soundcloud API /stream endpoint giving 401 error

I'm trying to write a react native app which will stream some tracks from Soundcloud. As a test, I've been playing with the API using python, and I'm able to make requests to resolve the url, pull the playlists/tracks, and everything else I need.
With that said, when making a request to the stream_url of any given track, I get a 401 error.
The current url in question is:
https://api.soundcloud.com/tracks/699691660/stream?client_id=PGBAyVqBYXvDBjeaz3kSsHAMnr1fndq1
I've tried it without the ?client_id..., I have tried replacing the ? with &, I've tried getting another client_id, I've tried it with allow_redirects as both true and false, but nothing seems to work. Any help would be greatly appreciated.
The streamable property of every track is True, so it shouldn't be a permissions issue.
Edit:
After doing a bit of research, I've found a semi-successful workaround. The /stream endpoint of the API is still not working, but if you change your destination endpoint to http://feeds.soundcloud.com/users/soundcloud:users:/sounds.rss, it'll give you an RSS feed that's (mostly) the same as what you'd get by using the tracks or playlists API endpoint.
The link contained therein can be streamed.
Okay, I think I have found a generalized solution that will work for most people. I wish it were easier, but it's the simplest thing I've found yet.
Use API to pull tracks from user. You can use linked_partitioning and the next_href property to gather everything because there's a maximum limit of 200 tracks per call.
Using the data pulled down in the JSON, you can use the permalink_url key to get the same thing you would type into the browser.
Make a request to the permalink_url and access the HTML. You'll need to do some parsing, but the url you'll want will be something to the effect of:
"https://api-v2.soundcloud.com/media/soundcloud:tracks:488625309/c0d9b93d-4a34-4ccf-8e16-7a87cfaa9f79/stream/progressive"
You could probably use a regex to parse this out simply.
Make a request to this url adding ?client_id=... and it'll give you YET ANOTHER url in its return json.
Using the url returned from the previous step, you can link directly to that in the browser, and it'll take you to your track content. I checked on VLC by inputting the link and it streams correctly.
Hopefully this helps some of you out with your developing.
Since I have the same problem, the answer from #Default motivated me to look for a solution. But I did not understand the workaround with the permalink_url in the steps 2 and 3. The easier solution could be:
Fetch for example user track likes using api-v2 endpoint like this:
https://api-v2.soundcloud.com/users/<user_id>/track_likes?client_id=<client_id>
In the response we can finde the needed URL like mentioned from #Default in his answer:
collection: [
{
track: {
media: {
transcodings:[
...
{
url: "https://api-v2.soundcloud.com/media/soundcloud:tracks:713339251/0ab1d60e-e417-4918-b10f-81d572b862dd/stream/progressive"
...
}
]
}
}
...
]
Make request to this URL with client_id as a query param and you get another URL with that you can stream/download the track
Note that the api-v2 is still not public and the request from your client probably will be blocked by CORS.
As mentioned by #user208685 the solution can be a bit simpler by using the SoundCloud API v2:
Obtain the track ID (e.g. using the public API at https://developers.soundcloud.com/docs)
Get JSON from https://api-v2.soundcloud.com/tracks/TRACK_ID?client_id=CLIENT_ID
From JSON parse MP3 progressive stream URL
From stream URL get MP3 file URL
Play media from MP3 file URL
Note: This link is only valid for a limited amount of time and can be regenerated by repeating steps 3. to 5.
Example in node (with node-fetch):
const clientId = 'YOUR_CLIENT_ID';
(async () => {
let response = await fetch(`https://api.soundcloud.com/resolve?url=https://soundcloud.com/d-o-lestrade/gabriel-ananda-maceo-plex-solitary-daze-original-mix&client_id=${clientId}`);
const track = await response.json();
const trackId = track.id;
response = await fetch(`https://api-v2.soundcloud.com/tracks/${trackId}?client_id=${clientId}`);
const trackV2 = await response.json();
const streamUrl = trackV2.media.transcodings.filter(
transcoding => transcoding.format.protocol === 'progressive'
)[0].url;
response = await fetch(`${streamUrl}?client_id=${clientId}`);
const stream = await response.json();
const mp3Url = stream.url;
console.log(mp3Url);
})();
For a similar solution in Python, check this GitHub issue: https://github.com/soundcloud/soundcloud-python/issues/87

How to make CORS API call from Blazor client app with authentication using AutoRest Client?

I am trying to call Web API from Blazor Client App. The API sends required CORS headers and works fine when I call the API using plain Javascript.
The API needs Auth cookies to be included when making a call so using JavaScript I can call:
fetch(uri, { credentials: 'include' })
.then(response => response.json())
.then(data => { console.log(data) })
.catch(error => console.log('Failed'));
Now, I am trying to do the same on Blazor. I came across this section on the docs which says:
requestMessage.Properties[WebAssemblyHttpMessageHandler.FetchArgs] = new
{
credentials = FetchCredentialsOption.Include
};
When I make a call now, it fails with following exception:
WASM: System.Net.Http.HttpRequestException: TypeError: Failed to execute 'fetch' on 'Window': The provided value '2' is not a valid enum value of type RequestCredentials.
I noticed that adding following on Statrup.cs allows me to call API including credentials (here):
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("WEBASSEMBLY")))
{
WebAssemblyHttpMessageHandler.DefaultCredentials = FetchCredentialsOption.Include;
}
Now, I would like to call the API using AutoRest generated API Client so that I can reuse existing client and save lot of time. Setting DefaultCredentials as above doesn't work and shows following exception:
WASM: Microsoft.Rest.HttpOperationException: Operation returned an invalid status code 'InternalServerError'
Setting the requestMessage.Properties as above, says
The provided value '2' is not a valid enum value of type RequestCredentials`.
I am already injecting HttpClient from Blazor using this technique.
This is not really the answer... I just need space
Setting the requestMessage.Properties as above, says The provided
value '2' is not a valid enum value of type RequestCredentials
If so, what is wrong with the other method I suggested, which I guess is working.
Incidentally,
The provided value '2' is not a valid enum value of type
RequestCredentials
is not related to Blazor, right ? No such type (RequestCredentials) in Blazor. Perhaps your code, whatever it may be, gets the numeric value of the FetchCredentialsOption.Include and not its Enum string
Consider instantiating an HttpRequestMessage object, and configuring it according to your requirements.
Hope this helps...

In meteor how do you make an API request

I am trying to get JSON data from SportsRadar using an API request. My trial url is:
http://api.sportradar.us/nba/trial/v4/en/games/2018/03/03/schedule.json?api_key=4j9ge4a4rgsbq597f29p9rgb
When I copy this url into my google browser, the data I get back is as expected, but when I try to use/add the API request to my meteor project the API request does not return any data. As a test, in my client/main.js file I have added:
HTTP.call('GET',Meteor.absoluteUrl("http://api.sportradar.us/nba/trial/v4/en/games/2018/03/03/schedule.json?api_key=4j9ge4a4rgsbq597f29p9rgb"),
function(err,result){
console.log(result.data);
});
The console log result come back as null. Any guidance or thoughts will be appreciated - cfp
You need to call your callback function correctly. Try this;
HTTP.call('GET','http://api.sportradar.us/nba/trial/v4/en/games/2018/03/03/schedule.json?api_key=4j9ge4a4rgsbq597f29p9rgb'),
function(err,result){
if (result) {
console.log(result.data);
}
console.log(err);
});
Edit: The parameters of The HTTP.call() is corrected by removing Meteor.absoluteUrl()in the question upon Derrick's comment below.
You can also refer to the official documentation here.

casperjs/slimerjs: get request headers

I'm trying to do a crawler using casperjs. Some requests need raw headers edition: I have to get the raw post data, cookies, etc etc, and once I get them, I'd like to modify them (still raw) and do another request with those modified headers. But I can't find a way to do that.
I've found how to retrieve cookies using Phantomjs, but I did not found anything in casperjs/slimerjs documentation.
Thank you for your help
You can listen for the page.resource.requested event and access the headers property of the requestData:
var casper = require('casper').create();
var utils = require('utils');
casper.start('https://example.com/');
casper.on('page.resource.requested', function (requestData, networkRequest) {
utils.dump(requestData.headers);
});
casper.run();

Using the new Bing API (nodejs)

I am trying to switch over to the new bing search api which is now hosted on azure and as most of you know that the Appid parameter has been removed for authentication, I can't figure out an easy way to get the results of the query.
I am using http module for nodejs and the current code uses http.get to query the api.bing.net
.
Can someone show me the code to use the new one correctly?
I tried this, but the url fails to return anything
http://gavinmhackeling.com/blog/2012/05/using-the-bing-search-api-in-python/
Sorry, there was a typo in my blog post.
Make sure that you are using your default account key from https://datamarket.azure.com/account/keys.
To use the basic authentication, replace <YourDefaultAccountKey> in https://user:<YourDefaultAccountKey>#api.datamarket.azure.com/Bing/SearchWeb/Web?Query=%27leo%20fender%27&Market=%27en-US%27&$top=50&$format=JSON. You will be returned results in JSON.
You can also use https://datamarket.azure.com/dataset/explore/5BA839F1-12CE-4CCE-BF57-A49D98D29A44 to test query parameters and formatting.
You can use this module that encapsulates the requests, so you can use it like:
var Bing = require('node-bing-api')({ accKey: "your-account-key" });
Bing.web("stack overflow", function(error, res, body){
console.log(body);
},
{
top: 50,
market: 'en-US'
});
It works with the Azure version. You only have to replace your account key.