I am interacting with a server (Geoserver) by sending a request to get some data.
The data is geographic information about some features.
Here is how I am doing it :
function filter() {
var demande = new XMLHttpRequest();
var url ='http://localhost:8080/geoserver/wfs?request=GetFeature&version=1.1.0&typeName=topp:refer_22&propertyName=Name,maticha&outputFormat=GML2&FILTER=%3CFilter%20xmlns=%22http://www.opengis.net/ogc%22%3E%3CPropertyIsBetween%3E%3CPropertyName%3Etopp:maticha%3C/PropertyName%3E%3CLowerBoundary%3E%3CLiteral%3E4500%3C/Literal%3E%3C/LowerBoundary%3E%3CUpperBoundary%3E%3CLiteral%3E5000%3C/Literal%3E%3C/UpperBoundary%3E%3C/PropertyIsBetween%3E%3C/Filter%3E'
demande.open("GET", url);
demande.onload=() => {
console.log(demande.response);
}
demande.send();
}
The request is to get data that match a condition ( x <data< some value )
I have a button when I click on it, the function filter() run to send and get the response of the xmlhttprequest.
Everything is working fine except that the result I get in the console is long :
<wfs:FeatureCollection
xmlns="http://www.opengis.net/wfs"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns:gml="http://www.opengis.net/gml"
xmlns:topp="http://www.openplans.org/topp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://localhost:8080/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd http://www.openplans.org/topp http://localhost:8080/geoserver/wfs?service=WFS&version=1.0.0&request=DescribeFeatureType&typeName=topp%3Arefer_22,topp%3Arefer_22">
<gml:boundedBy>
<gml:null>unknown</gml:null>
</gml:boundedBy>
<gml:featureMember>
<topp:refer_22 fid="refer_22.9">
<topp:the_geom>
<gml:MultiPolygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
<gml:polygonMember>
<gml:Polygon>
<gml:outerBoundaryIs>
<gml:LinearRing>
<gml:coordinates
xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">34.69647,-1.919276 34.69651,-1.918997 34.697229,-1.919003 34.697317,-1.918058 34.697418,-1.91713 34.697462,-1.91653 34.697568,-1.915618 34.697837,-1.914641 34.698261,-1.913174 34.698614,-1.912023 34.699005,-1.910633 34.697456,-1.909919 34.696871,-1.909666 34.695711,-1.90917 34.694404,-1.908606 34.693794,-1.908351 34.69322,-1.908068 34.69202,-1.907558 34.691892,-1.908461 34.691702,-1.909658 34.69146,-1.910865 34.691429,-1.911192 34.691252,-1.912179 34.691063,-1.913338 34.690979,-1.913724 34.690851,-1.914132 34.690772,-1.914518 34.690644,-1.915205 34.690489,-1.915998 34.690145,-1.91764 34.690017,-1.918244 34.689918,-1.918702 34.689845,-1.919067 34.6897,-1.919978 34.689386,-1.92124 34.689422,-1.923584 34.69097,-1.923686 34.692518,-1.923552 34.6928,-1.923519 34.69329,-1.923428 34.693444,-1.923401 34.694662,-1.923037 34.695372,-1.922404 34.695663,-1.922034 34.695943,-1.921564 34.696161,-1.921111 34.696298,-1.920569 34.696399,-1.920022 34.696444,-1.91966 34.69647,-1.919276
</gml:coordinates>
</gml:LinearRing>
</gml:outerBoundaryIs>
</gml:Polygon>
</gml:polygonMember>
</gml:MultiPolygon>
</topp:the_geom>
<topp:Name>OUJ-DK9</topp:Name>
<topp:maticha>4500</topp:maticha>
</topp:refer_22>
</gml:featureMember>
</wfs:FeatureCollection>
As you can see, this above is the result in string format. There is a 'Geometry' field (the_geom), a 'name' field and a field called 'maticha' (used to filter the data).
This is good but I would rather get a SON format for the data or some kind of array where I can access it.
For example, I want to be able able to write :
var geometry = response[..]
to get the list of coordinates of the returned result.
Is there any way to that ??
Geoserver supports different result formats with the outputFormat parameter. Your request sets it to GML2, which is XML. JSON output is also supported.
Since your request URL is really hard to read and to manipulate, I recommend that you use a UrlSearchParams object instead of trying to manage the URL parameters manually in one huge messy line.
Here is how I would implement your filter() function.
function filter(lowerBoundary, upperBoundary, onSuccess) {
var params = new URLSearchParams();
params.append('request', 'GetFeature');
params.append('version', '1.1.0');
params.append('typeName', 'topp:refer_22');
params.append('propertyName', 'Name,maticha');
params.append('outputFormat', 'application/json');
params.append('filter', `<Filter xmlns="http://www.opengis.net/ogc">
<PropertyIsBetween>
<PropertyName>topp:maticha</PropertyName>
<LowerBoundary><Literal>${lowerBoundary}</Literal></LowerBoundary>
<UpperBoundary><Literal>${upperBoundary}</Literal></UpperBoundary>
</PropertyIsBetween>
</Filter>`);
var demande = new XmlHttpRequest();
demande.open("GET", 'http://localhost:8080/geoserver/wfs?' + params.toString());
demande.onload = () => onSuccess(JSON.parse(demande.response));
demande.send();
}
// later ...
filter(4000, 5000, (data) => {
console.log(data):
});
There also seems to be an easier filter syntax available, see ECQL.
Related
Using browser.webRequest.onBeforeRequest.addListener() I am able to filter HTTP requests and modify the source of pages before they are returned. I need to intercept HTTP POST requests and modify the data submitted by a form before sending it to the server. Check the code:
browser.webRequest.onBeforeRequest.addListener(
function(details) {
// The code in this if is just some experimentation.
if(details.method == "POST") {
let filter = browser.webRequest.filterResponseData(details.requestId)
let decoder = new TextDecoder("utf-8")
let encoder = new TextEncoder()
filter.ondata = event => {
let str = decoder.decode(event.data, {
stream: true
});
console.log(str)
}
// This is a where I attempt to modify POST data.
let formData = details.requestBody.formData;
//console.log(formData)
if(formData) {
Object.keys(formData).forEach(key => {
formData[key].forEach(value => {
//console.log(key)
if(key == 'testField'){
//console.log("found", formData[key])
details.requestBody.formData[key] = "INJECTED"
console.log('injected', details.requestBody.formData[key])
}
})
})
}
return {details: details};
}
},
{urls: ["https://example.com/*"]},
["blocking", "requestBody"]
);
The last console.log prints the modified value but returning details does not forward the modified value to the server. How can I use onBeforeRequest (Or any other method) in a Firefox extension to modify POST variables before they are sent to the server?
I do not want to inject code into the web page. I want to filter the traffic.
This seems to be a missing feature.
Relevant bugs:
https://bugzilla.mozilla.org/show_bug.cgi?id=1376155
https://bugs.chromium.org/p/chromium/issues/detail?id=91191
This question already has answers here:
XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header
(11 answers)
Closed 3 years ago.
I am trying to get data from AWS JSON. It is not on my domain and I can't get the data. The error that chrome gives is below Could anyone review my code and give me some tips?
Second, I was using Insomnia just using the link and was able to get the specific data point that I wanted using $.0.Ed0320. How do I translate this into JavaScript.
I have tried using a xmlHttpRequest.
Here is the JSON:
[
{
"Ed0320": "8.010886",
"TmStamp": "2019-08-07 15:15:00",
"Ed0340": "21.15973",
"Ed0305": "0.2966875",
"Ed0313": "3.344086"
},
{
"Ed0320": "6.761719",
"TmStamp": "2019-08-07 15:10:00",
"Ed0340": "17.47292",
"Ed0305": "0.2349026",
"Ed0313": "2.789934"
}
]
Here is my XML:
function reqListener() {
// parse the the data
var data = JSON.parse(this.responseText)
// create an array from the required data (result.Stats -> watch the capital 'S')
var mappedData = data[0];
// display data
document.getElementById('demo').innerHTML = mappedData.join()
}
function loadData(url) {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", url);
oReq.send();
}
Chrome gives the error of Access to XMLHttpRequest at <fileName> from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
function calcUVI(Ed0305, Ed0313, Ed0320, Ed0340){
total305=(Ed0305*0.8058)
total313=(Ed0313*0.0887)
total320=(Ed0320*0.0324)
total340=(Ed0340*0.0131)
UVI = total305 + total313 + total320 + total340
return UVI
}
Also, I would like to change the url based on this function below. Is there a way to input this into the fetch?
function changeText(){
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth() + 1;
var day = d.getDate();
if (month <= 9){
month = '0'+month;
}
if (day <= 9){
day = '0'+day;
}
var dateTotal = year + month + day;
url = "https://cors-escape.herokuapp.com/https://tepfsail50.execute-api.us-west-2.amazonaws.com/v1/report/metguv?rptdate=" + dateTotal;
console.log(url);
}
Thank you in advance for your help. I am really new to all of this.
1. Fetching the data
I'm not quite sure, what you want to achieve, but here's a snippet, that
Makes a request to cors-anywhere.herokuapp.com to "proxy" the query of the "target" URL
Returns a Promise from the queried URL, so we can wait for it to resolve in other parts of the code
Filter and map the resulting data - I didn't understand what data would you like to get from the resulting array and how did you want to handle that data
// self calling function to wrap the await in
// an async function (so the await is not "top level")
(async function() {
// waiting for the returned Promise to resolve
// if you console.log() the data constant, then you'll
// see that it's the full dataset (in JSON format) that you
// got from simply calling the AWS URL
const data = await fetchURL()
// filtering the array for objects that have a key of "Ed0320"
const filtered = data.filter(e => typeof e['Ed0320'] !== "undefined")
// mapping the values of all "Ed0320" from the array
const mapped = data.map(e => e['Ed0320'])
// simple output, so you can check the values
console.log(filtered);
console.log(mapped)
})();
// fetch request using cors-anywhere
function fetchURL() {
// creating the URL to be called:
// first part is cors-anywhere, second part is
// the real URL to be queried
const url = 'https://cors-anywhere.herokuapp.com/https://tepfsail50.execute-api.us-west-2.amazonaws.com/v1/report/metguv?rptdate=20190807'
// returning a Promise, as fetch() is an asynchron
// function, so it's not resolved immediately
return new Promise((resolve, reject) => {
// using fetch() instead of the XMLHttpRequest()
// fetch returns a Promise, so you can use .then() and
// .catch() chained after it
fetch(url)
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
}
So, your code had CORS issues (CORS: Cross-Origin Resource Sharing), that had to be handled before you could get the data. I used the cors-anywhere on Heroku to overcome this obstacle. You can set your cors-anywhere service by installing the module from npm (here: https://www.npmjs.com/package/cors-anywhere).
2. EDIT: Processing the data
The answer is edited as the question was expanded with more information.
This new snippet does calculate the UVI based on the results from the API (the URL is generated dynamically based on current date).
Please note:
you can make all your functions shorter (like don't create variables for values that you only need for one calculation, where you only return the result - e.g. current date string or the UVI calculation)
the "creation" of the date string is modified: I used string interpolation and ('0' + d.getDate()).slice(-2) to force two digits long months and days
when you create (design) a function try to keep it simple - it should do only one thing (so don't create a function that generates current date string and attaches it to the query string. Maintenance will be much easier this way.
the calcUVI() function receives its parameters via destructuring the array element it's called with. Get familiar with destructuring - it's not that hard, and makes your code easier to read and understand. (You can start here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
(async function() {
// modified fetchURL() - the URL parameter is passed to
// the API query function
const data = await fetchURL(urlToQuery())
// calling calcUVI() function with the first data
// entry of the data array
const UVI = calcUVI(data[0])
// displaying result in the console
console.log("UVI:", UVI)
})();
function fetchURL(url) {
return new Promise((resolve, reject) => {
// the url parameter is received when the function
// is called - it's not a constant anymore
fetch(url)
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
}
// creating and returning the API URL dynamically -
// date string is added
function urlToQuery() {
// the query URL base is here - and the date string
// at the end is created dynamically
return `https://cors-anywhere.herokuapp.com/https://tepfsail50.execute-api.us-west-2.amazonaws.com/v1/report/metguv?rptdate=${getDateString()}`
}
// creating and returning the date string
function getDateString() {
const d = new Date();
return `${d.getFullYear()}${('0' + (d.getMonth() + 1)).slice(-2)}${('0' + d.getDate()).slice(-2)}`
}
// calcUVI receives the parameters destructured from
// the first element of the array (it's called in
// the main async function)
function calcUVI({
Ed0305,
Ed0313,
Ed0320,
Ed0340
}) {
// no need to store these elements as variables, as the single
// Ed... values are not used anymore in the process (a
// total is returned, so just add them up)
return Ed0305 * 0.8058 + Ed0313 * 0.0887 + Ed0320 * 0.0324 + Ed0340 * 0.0131
}
I have this form that executes blogSearch() on submit:
<form onsubmit="return blogSearch();">
<input class="mb0" type="text" id="blogsearchinput" placeholder="Search..." />
</form>
Here is my javascript:
function blogSearch() {
var test = document.getElementById("blogsearchinput").value
window.location.href = "../list.php";
return false;
}
I want to run some more javascript in the function blogSearch() after the page loads, but obviously I can't do that after return false. The page will only load if I return false because I read that that will override the default input submit.
Is there are a way to load a URL from an input submit without needing return false? Or some way I can continue running a function after loading?
You can't display search results on the same page using window.location, that
takes you to another page. In this case, you probably want to use a
XHR request to
get the data and then display it on the same page.
I've modified the HTML a little:
<form id="search-form">
<label>Search blog: <input class="mb0" type="search" id="search-query"></label><input type="submit" value="Search">
</form>
<ul id="search-results"></ul>
Then in list.php you would do something like this:
<?php
// get the q parameter that we will be setting in our XHR request
$query = isset($_GET['q']) ? $_GET['q'] : '';
// I'm assuming you are returning a list of links, so I'm creating an
// array of associative arrays that contain url and link name info.
// In your actual application you would probably be pulling this data from a
// database instead, using the $query variable to filter the results that the DB
// returns. I would recommend using prepared statements to avoid SQL injection
// attacks. My PHP is really rusty right now so I'm not going to attempt to
// filter this array and just return the whole thing.
$results = array(
array('name' => 'Foo', 'url' => '/foo'),
array('name' => 'Bar', 'url' => '/bar'),
array('name' => 'Baz', 'url' => '/baz'),
);
// encode our array as JSON so we can easily decode it in JavaScript
echo (json_encode($results));
?>
Then, here is the JS you would use to make the request and display the results.
// cache elements we will be using in variables
const searchForm = document.getElementById("search-form");
const searchQuery = document.getElementById("search-query");
const searchResults = document.getElementById("search-results");
// this function will do the XHR request and display the results
function blogSearch(event) {
// stops the default action, in this case submitting the form to the server
event.preventDefault();
// get the query and build a url to request
let query = searchQuery.value;
let url = `../list.php?q=${query}`;
let displayError = () => {
searchResults.innerHTML = '<li>There was an error retrieving your search results</li>';
};
let request = new XMLHttpRequest();
request.open('GET', url, true);
request.onload = () => {
if (this.status >= 200 && this.status < 400) {
// parse the JSON data that was returned
let data = JSON.parse(this.response);
// build up a list of li element containing links
let html = ''
data.forEach(item => {
html += `<li>${item.name}</li>`;
});
// display the search results in our unordered list
searchResults.innerHTML = html;
} else {
displayError();
}
};
request.onerror = () => {
displayError();
};
request.send();
}
// attach the event to the search form
searchForm.addEventListener('submit', blogSearch, false);
A few notes on things I changed:
Unless you are using XHTML, you don't need to self-close void elements. (i.e. in my code I used <input> instead of <input />).
I used a label instead of a placeholder because placeholders are meant for an example of valid input, not as a label. I also used type="search" instead of text since this is meant to be a search box.
I used addEventListener instead of an onsubmit to maintain separation of concerns.
I used arrow functions; all modern browsers support them (IE isn't modern).
I used a template literal; modern browsers support them (again IE is not modern).
I used the modern let and const instead of var as any remotely modern browser supports them (even IE 11 supports them).
Further reading
preventDefault on MDN
XHR code adapted from example at YouMightNotNeedjQuery.com
Prepared statement with MySQLi
SQL injection at Wikipedia
SQL injection at OWASP
Obligatory Bobby tables reference
bobby-tables.com
I'm trying to convert a Parse.com object into an image with Javascript. The object is of the type Bytes, and I can't convert it in any way. Can someone give me an example of how to turn it into a functional URL? I try to access it but it just keeps crashing.
Edit:
getURL: function () {
var query = new Parse.Query("Token");
query.limit(20);
query.find().then(this.handleCallback.bind(this));
},
handleCallback: function (objects) {
for (var i = 0; i < objects.length; i++) {
this.tokenSearch[i] = {
imgURL: "data:image/png;base64," + objects[i].get("pic")
};
}
}
I have tried objects[i].get("pic").url(), objects[i].get("pic").toString('base64') and some other stuff, but it won't work!
If you want to store images and later have a URL you can load, you should switch to the File data type instead of pushing just a byte array. The documentation you linked recommends the following:
There are two ways to store binary data. The Bytes type allows you to associate NSData/bytes[] types directly on a PFObject. This is recommended only for small pieces of binary-encoded data. For actual files (images, documents, etc.), the File type can be used by instantiating a PFFile/ParseFile and setting it on a field.
You have stored the actual byte array into the pic column of your Parse class. Therefore, that's all you get. If you chose instead to store it as a File (PFFile in iOS/OSX, ParseFile in Android), you could do what you're trying to do. As it is, Parse is merely storing the bytes as it would store any other data, such as a Date or a String. There's no URL data associated with it.
Here's how you can create and upload a PFFile from an iOS device:
- (void)updateloadImage:(UIImage *)image {
PFFile *file = [PFFile fileWithData:UIImagePNGRepresentation(image)];
PFObject *newToken = [PFObject objectWithClassName:#"Token"];
newToken[#"pic"] = file;
[newToken saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// Do real error handling here
// Now, you have :
// 1. The "pic" saved to Parse as a PFFile
// 2. The "token" saved to Parse as a PFObject
// 3. The "pic" stored as a property on the "token"
}
];
}
Once you have it stored that way on Parse, you would access it in the JavaScript SDK something like this:
function getURL() {
var query = new Parse.Query("Token");
query.limit(20);
query.find().then(function (objects) {
for (var i = 0; i < objects.length; i = i + 1) {
this.tokenSearch[i] = {imgURL: objects[i].get("pic").url()};
}
});
}
This seems silly that I haven't been able to accomplish this. I'm using the common code found on the web to do an odata query. The problem is the results stay in getFieldData(retrieveReq) routine. I don't want to immediately set a field on the current form. How can I get my values out of it so the data can be used in other javascript functions? Global variable would be good but nothing I've tried has worked. The below code displays "x".
var var1 = "x"; odataquery(); alert(var1);
The example given here has two alerts that display the data. How can Id and Name get outside of that function to be useful?
Edit1: Below is the main part of the routine that calls getFieldData(this). I want to use OwnerBUID and OwnerBUName in other javascript functions.
var retrieveReq = new XMLHttpRequest();
retrieveReq.open("GET", odataSelect, false);
retrieveReq.setRequestHeader("Accept", "application/json");
retrieveReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
retrieveReq.onreadystatechange = function () {
getFieldData(this);
};
retrieveReq.send();
function getFieldData(retrieveReq) {
if (retrieveReq.readyState == 4 && retrieveReq.status == 200) {
// 4=request complete, 200=OK
var retrieved = this.parent.JSON.parse(retrieveReq.responseText).d;
var retrievedValue = retrieved.results[0].BusinessUnitId;
OwnerBUID = retrievedValue.Id;
OwnerBUName = retrievedValue.Name;
}
}
I guess you want to put the data as the return value of a javascript function. You could do this:
var returnedData = function getFieldData(retrieveReq)
{
...
return data;
}
BTW, you could consider to use JayData, Breeze and Datajs sources code packages in your client application. They implement the low level APIs for consuming an odata service using javascript.