Inject html as the result of an API Call Chrome Extension - javascript

The Question
How do I take the json result from and API call and inject the result into and html page?
The Context
As part of a web extension I am working on, I call and API for a resulting json.
my popup.html has a button click that calls a call.js file.
call.js (example)
$.ajax({
type: 'GET',
url:body,
headers: headers,
success:function(data) {
console.log(data);
// use return json to update html
// maybe open html for injection here as
// var url = chrome.extension.getURL('location.html')
// chrome.tabs.create({url: url, active: true});
}
Interms of flow, i'm not sure how to pass the result of the api call, in and effective way, to a scrip that will inject HTML into the location.html file.
I tried the following flow
Get API Json -> store json in localStorage -> open location.html url
-> have a content script that runs an injection.js file one https://*location.html* appears -> inject required elements
but the json was to large for local storage, and honestly, I'm unsure if that flow was even correct. Do you have any suggestions on how to resolve this issue? I feel this is a standard functionality of web extensions, but for some reason, I cannot put this piece of the puzzle together.
Thanks!

use massage passing.
in your content script
chrome.runtime.onMessage.addListener(req =>{
if(req.action === "showJSONFromPopup"){
showJSONResult(req.data)
}
})
in your popup script
$.ajax({
type: 'GET',
url:body,
headers: headers,
success:function(data) {
console.log(data);
chrome.tabs.query({currentWindow:true, active:true}, tabs=>{
chrome.tabs.sendMessage(tabs[0].id, {action: "showJSONFromPopup", data})
})
}

Related

Request to Google Apps Script URL for deployed WebApp produces 404 error

This issue is very similar to others (like Google Drive Page Not Found - Sorry, unable to open the file at this time for example) posted here. It's not exactly the same, but I do believe it has the same root issue illustrated in that post in that trying to submit a form to a Google App Script while logged into more than to 1 Google account causes /u/1 and/or /u/0 to be added to the script's URL thus producing a 404 Error.
This is using a standard Google account - not G-Suite.
I have a form on a website that submits to a Google Apps Script via AJAX. The script makes some API calls to create a Google Doc containing the data collected by the form.
HTML/Javascript:
<form>
<input type="text" name="full_name">
<input type="text" name="phone">
<input type="submit">
</form>
$('form').submit(function() {
var obj = $(this).serializeObject();
var gurl = "https://script.google.com/macros/s/AKfycbzmhaub3ojPARA-B-Y2uVC2BJZPaRvbgMwMTH9pd7R9aHuAD5M/exec";
$.ajax({
url: gurl,
type: "GET",
data: obj,
dataType: "jsonp",
success: function(data, status, xhr) {
console.log("success");
console.log(data);
});
});
GoogleScripts
function doGet(e) {
var params = e.parameters
var result = {};
try {
result = {
status: start(params),
msg: 'Success',
vals: formData,
rawVals: params,
errs: errors
}
} catch (f) {
result.error = f.toString();
}
return ContentService
.createTextOutput(e.parameters.callback + '(' + JSON.stringify(result) + ')')
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
Submitting the form while logged into more than 1 Google account in the same browser results in the following error in the console and the form does nothing:
jquery.js?ver=1.12.4-wp:4 GET
https://script.google.com/macros/u/1/s/AKfycbzmhaub3ojPARA-B-Y2uVC2BJZPaRvbgMwMTH9pd7R9aHuAD5M/exec?callback=jQuery112407830193282901534_1608623376571&s&full_name=Dave+Pe&phone=1111111111_=1608623376572
net::ERR_ABORTED 404
When I go to Network tab to view the request, the Header tab there shows the following:
Request URL: https://script.google.com/macros/u/1/s/AKfycbzmhaub3ojPARA-B-Y2uVC2BJZPaRvbgMwMTH9pd7R9aHuAD5M/exec?callback=jQuery112407830193282901534_1608623376571&s&full_name=Dave+Pe&phone=1111111111_=1608623376572
Notice the /u/1/ that have been inserted into the URL that are not present in the URL I pass to my $.ajax() call.
Most of the answers I've found for this issue say to just remove the /u/1/, but since I didn't add it in the 1st place, I don't know where I would remove it from.
Can anyone confirm that this seemingly known issue (of having the URL altered when logged into multiple Google accounts) is what is causing my problems? Any ideas as to how I can go about making my request to:
https://script.google.com/macros/s/AKfycbzmhaub3ojPARA-B-Y2uVC2BJZPaRvbgMwMTH9pd7R9aHuAD5M/exec
and not
https://script.google.com/macros/u/1/s/AKfycbzmhaub3ojPARA-B-Y2uVC2BJZPaRvbgMwMTH9pd7R9aHuAD5M/exec
?? or is there something more deeply wrong with the way I'm trying to use Google Scripts here?
My solution was to move the request from the client side to the server. I submit my form values to a server-side page via AJAX and then on that page, I make an HTTP request with cURL to my Google Apps Script (sending the form data in the body) and send the response back to the client.
Seems to be working... no issues I can think of but that doesn't mean they don't exist. If there are any holes to be shot in that approach, please feel free to unload.
Watered down...
JavaScript
$.ajax({
url: '/ajax-handler.php',
type: "POST",
data: obj
success: function(data, status, xhr) {
console.log("success");
console.log(data);
});
});
PHP
$vals = my_sanitize_func($_POST);
$url = GAS_URL;
$fields = $vals;
//open connection
$ch = curl_init();
//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, http_build_query($fields));
//execute post
$result = curl_exec($ch);
//close connection
curl_close($ch);
// handle/format response;
$result = ...
print $result;
You can also do whatever you want this way.
Google App Script (Code.gs):
function doGet(e){
var action = e.parameter.action;
if (action=="sendSuccess"){
justSendSuccess(e);
}
}
function justSendSuccess(e){
var output = JSON.stringify({"result":"success"});
return ContentService
.createTextOutput(e.parameter.callback+"("+ output + ");")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
JavaScript Part:
function callGoogleScript(){
console.log("Started");
var url = "https://script.google.com/macros/s/###SCRIPT_ID###/exec";
$.ajax({
crossDomain: true,
url: url,
data: {
"action":"setInfo"
},
method: "GET",
dataType: 'jsonp',
jsonp: "callback",
success: function(data){
console.log("SUCCESS!");
console.log(data);
}
});
}
I tried to run it (authenticated with a single account) and it seems to work. This error seems to happen because you are authenticated with multiple accounts. Also, it seems as it has already been documented at Google Issue Tracker (link to issue). If you want it to make it more visible, you can click the white star (☆) which tells google that you are affected by this issue.
As a side note, notice that the code you make will be executed by everyone as you. This code will have your privileges. Be very careful. Your account limits may also apply.

Wikipedia API. File not found Error

I'm trying to make a wikipedia search bar. The idea is to send a new AJAX request every time search input is changed. I'm using https://www.mediawiki.org/wiki/API:Search_and_discovery as a guideline.
var search = $('#search');
search.keyup(function() {
if (search.val() === '') {
result.html('');
}
$.ajax({
url: '//en.wikipedia.org/w/api.php',
data: {
action: 'query',
list: 'search',
format: 'json',
srsearch: search.val()
},
dataType: 'jsonp',
success: function(response) {
console.log("success!");
}
});
});
However, success function is not even triggered.
On any keypress the error I get is this ("d" pressed):
jquery-2.1.1.min.js:4 GET file://en.wikipedia.org/w/api.php?>callback=jQuery21107844703783826772_1484403407494&action=query&list=search&srse>arch=d&format=json&_=1484403407495 net::ERR_FILE_NOT_FOUND
Thank you in advance for any help or guidance!
Well, you're probably trying to do a AJAX request without a local server (opening your file directly in the browser).
First of all, your url options starts with //en... (without the protocol). It indicates that it'll construct your full url using the same protocol you're using. In this case: file://. That's because your browser is trying to reach file://en.wikipedia.org/....
So, you can set your url to https://en.wikipedia.org/w/api.php to make it work.
Just replace:
url: '//en.wikipedia.org/w/api.php',
with:
url: 'https://en.wikipedia.org/w/api.php',
Looks like you're running it from a simple html file located in your filesystem, in other words not running it from a web server (even local).
Try calling the api with
url: 'https://en.wikipedia.org/w/api.php'
or run the file from a web server (can be a local one).

W3C Validator offline on localhost by ajax

I use to programming Brackets text editor and I have already installed W3C validator but it working while I'm online but offine not. I try install https://validator.w3.org/docs/install.html and I running to localhost:8888 but brackets's extension connecting only via ajax (javascript). Is possible send ajax like to original W3C website?
Maintainer of the W3C HTML checker (validator) here. Yes it is possible to send an ajax request to a local instance of the current checker. To use Fetch to do it and get JSON-formatted results back:
var checkerUrl = "http://localhost:8888/?out=json"
fetch(document.location.href)
.then(function(currentDoc) { return currentDoc.text(); })
.then(function(htmlSource) {
fetch(
checkerUrl, {
method: "POST",
mode: "cors",
body: htmlSource,
headers: new Headers({ "Content-Type": "text/html;charset=utf-8" })
})
.then(function(checkerResponse) { return checkerResponse.json(); })
.then(function(jsonOutput) {
console.dir(jsonOutput.messages);
})
});
That shows the basic steps to follow to deliver the request in the way the checker expects:
send a document to the checker as the body of a POST (in this example, the current doc)
tell the checker to format its results as JSON (out=json)
make text/html;charset=utf-8 the media type of the POST body you send to the checker
The checker also supports multipart/form-data for giving it the HTML source to be checked, but giving it the source as a POST body instead is the preferred (and better) way for doing it.
If instead of using fetch() you want to use JQuery $.ajax(…), here’s an example:
var checkerUrl = "http://localhost:8888/?out=json"
$.get(document.location.href,
function(htmlSource)
{
$.ajax({
url: checkerUrl,
type: "POST",
crossDomain: true,
data: htmlSource,
contentType: "text/html;charset=utf-8",
dataType: "json",
success: function (jsonOutput) {
console.dir(jsonOutput.messages);
}
});
});
And if instead of either fetch() or JQuery $.ajax(…) you want to use old-school XHR but it’s not clear how to handle the details in that case, let me know and I can post an example of that too.
In all cases, the .messages JSON output is an array of objects that each contain something like:
firstColumn: 1
lastColumn: 6
lastLine: 4
message: "Unclosed element “span”."
type: "error"
The documentation for the checker JSON format gives more details of the JSON the checker emits.

Jquery Ajax call with GET parameters won't return nothing

I want to retrive a detail of a video throught the redtube API. if i copy and paste the link in the browser all works fine
http://api.redtube.com/?data=redtube.Videos.getVideoById&video_id=15485&output=json&thumbsize=all
Note: May be offensive to some users, it's a porn API.
But when i attempt to receive the json object throught an ajax call, no result is given and the call goes in error
$.ajax({
url : 'http://api.redtube.com/?data=redtube.Videos.getVideoById&video_id=15485&output=json&thumbsize=all',
dataType : 'json',
success: function (res)
{
alert (res)
},
error : function (res) {alert(res.code);}
})
Why?
You can't make AJAX call to the API due to same-origin policy. Learn more about it here

AD FS 2.0 Authentication and AJAX

I have a web site that is trying to call an MVC controller action on another web site. These sites are both setup as relying party trusts in AD FS 2.0. Everything authenticates and works fine when opening pages in the browser window between the two sites. However, when trying to call a controller action from JavaScript using the jQuery AJAX method it always fails. Here is a code snippet of what I'm trying to do...
$.ajax({
url: "relyingPartySite/Controller/Action",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});
The issue is that AD FS uses JavaScript to post a hidden html form to the relying party.
When tracing with Fiddler I can see it get to the AD FS site and return this html form which should post and redirect to the controller action authenticated. The problem is this form is coming back as the result of the ajax request and obviously going to fail with a parser error since the ajax request expects json from the controller action. It seems like this would be a common scenario, so what is the proper way to communicate with AD FS from AJAX and handle this redirection?
You have two options.
More info here.
The first is to share a session cookie between an entry application (one that is HTML based) and your API solutions. You configure both applications to use the same WIF cookie. This only works if both applications are on the same root domain.
See the above post or this stackoverflow question.
The other option is to disable the passiveRedirect for AJAX requests (as Gutek's answer). This will return a http status code of 401 which you can handle in Javascript.
When you detect the 401, you load a dummy page (or a "Authenticating" dialog which could double as a login dialog if credentials need to be given again) in an iFrame. When the iFrame has completed you then attempt the call again. This time the session cookie will be present on the call and it should succeed.
//Requires Jquery 1.9+
var webAPIHtmlPage = "http://webapi.somedomain/preauth.html"
function authenticate() {
return $.Deferred(function (d) {
//Potentially could make this into a little popup layer
//that shows we are authenticating, and allows for re-authentication if needed
var iFrame = $("<iframe></iframe>");
iFrame.hide();
iFrame.appendTo("body");
iFrame.attr('src', webAPIHtmlPage);
iFrame.load(function () {
iFrame.remove();
d.resolve();
});
});
};
function makeCall() {
return $.getJSON(uri)
.then(function(data) {
return $.Deferred(function(d) { d.resolve(data); });
},
function(error) {
if (error.status == 401) {
//Authenticating,
//TODO:should add a check to prevnet infinite loop
return authenticate().then(function() {
//Making the call again
return makeCall();
});
} else {
return $.Deferred(function(d) {
d.reject(error);
});
}
});
}
If you do not want to receive HTML with the link you can handle AuthorizationFailed on WSFederationAuthenticationModule and set RedirectToIdentityProvider to false on Ajax calls only.
for example:
FederatedAuthentication.WSFederationAuthenticationModule.AuthorizationFailed += (sender, e) =>
{
if (Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
{
e.RedirectToIdentityProvider = false;
}
};
This with Authorize attribute will return you status code 401 and if you want to have something different, then you can implement own Authorize attribute and write special code on Ajax Request.
In the project which I currently work with, we had the same issue with SAML token expiration on the clientside and causing issues with ajax calls. In our particular case we needed all requests to be enqueud after the first 401 is encountered and after successful authentication all of them could be resent. The authentication uses the iframe solution suggested by Adam Mills, but also goes a little further in case user credentials need to be entered, which is done by displaying a dialog informing the user to login on an external view (since ADFS does not allow displaying login page in an iframe atleast not default configuration) during which waiting request are waiting to be finished but the user needs to login on from an external page. The waiting requests can also be rejected if user chooses to Cancel and in those cases jquery error will be called for each request.
Here's a link to a gist with the example code:
https://gist.github.com/kavhad/bb0d8e4a446496a6c05a
Note my code is based on usage of jquery for handling all ajax request. If your ajax request are being handled by vanilla javascript, other libraries or frameworks then you can perhaps find some inspiration in this example. The usage of jquery ui is only because of the dialog and stands for a small portion of the code which could easly be swapped out.
Update
Sorry I changed my github account name and that's why link did not work. It should work now.
First of all you say you are trying to make an ajax call to another website, does your call conforms to same origin policy of web browsers? If it does then you are expecting html as a response from your server, changedatatype of the ajax call to dataType: "html", then insert the form into your DOM.
Perhaps the 2 first posts of this serie will help you. They consider ADFS and AJAX requests
What I think I would try to do is to see why the authentication cookies are not transmitted through ajax, and find a mean to send them with my request. Or wrap the ajax call in a function that pre authenticate by retrieving the html form, appending it hidden to the DOM, submitting it (it will hopefully set the good cookies) then send the appropriate request you wanted to send originally
You can do only this type of datatype
"xml": Treat the response as an XML document that can be processed via jQuery.
"html": Treat the response as HTML (plain text); included script tags are evaluated.
"script": Evaluates the response as JavaScript and evaluates it.
"json": Evaluates the response as JSON and sends a JavaScript Object to the success callback.
If you can see in your fiddler that is returning only html then change your data type to html or if that only a script code then you can use script.
You should create a file anyname like json.php and then put the connection to the relayparty website this should works
$.ajax({
url: "json.php",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});

Categories

Resources