I have a Javascript component that when the DOM is loaded it needs to send a request out to our CDN, which may be in a different domain, to see if there is content for this component. If there is, the component will self-instantiate (its a link to open an embedded video in a modal), if not it will self destruct. My question is mainly about the Grails controller I am using to proxy the AJAX request.
Here is the JS in pseudocode:
checkForVideoAssets: function(videoDataUrl){
Ajax.get(videoDataUrl, function(data){
if(data.responseText==='error'){
//tear down the component
}
else{
//if there is data for the video instantiate the component
}
Here is the Grails controller:
def checkForModalVideoAsset = {
def req = new URL("http://" + params.videoUrl + "/expense/videos/")
def connection = req.openConnection()
if(connection.responseCode != 200){
render 'error'
}
if(connection.responseCode == 200){
render req.getText()
}
}
So, to sum up, the JS grabs an attribute from the DOM that has part of a URL (that we define by convention), sends that URL to the controller, the controller attempts to connect to that URL (at our CDN) and then passes that response back to the AJAX success callback inside the responseText part of the XHR object. This feels less than ideal to me, is it possible to pass the actual response back up to the JS function?
The httpbuilder may be usefull to you
I never tried it but something similar!?
def checkForModalVideoAsset = {
def http = new HTTPBuilder("http://" + params.videoUrl )
http.get(
path : "/expense/videos/",
contentType : TEXT ) { resp, reader ->
response.properties=resp.properties //<-- to easy to work but why not try :)
response << resp
}
}
Related
Chrome extensions have the ability to intercept all web requests to specified URLs using chrome.webRequest.onBeforeRequest. This includes not only static asset requests, but requests for AJAX, PJAX, favicons, and everything in between.
Apple provides a few close approximations to this functionality, such as the beforeLoad (handles images, CSS, and JS) and beforeNavigate (handles full page loads) event handlers, but neither catch AJAX requests. I've tried overloading XMLHttpRequest in an attempt to catch AJAX loads to no avail (I might be doing something wrong). Here's a brief example of how I'm doing this:
var originalOpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
console.log("overriden");
return originalOpen.apply(this, arguments);
}
How can I catch all web requests (AJAX, CSS, JS, etc.) in a Safari extension?
Update: You can check entire code flow on my first Safari Extension I've wrote for TimeCamp tracker: https://github.com/qdevro/timecamp.safariextz
I have succeeded to intercept all AJAX calls (actually the responses were interesting for me, because there all the magic happens), but unfortunately I couldn't find (yet) a solution to send it back to my injected script (I still work on this) now fully working - getting the xhr to the injected script:
I've done it like this:
1) on the injected START script, I've added into the DOM another script (the one which does the interception):
$(document).ready(function(){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = safari.extension.baseURI + 'path/to/your/script/bellow.js';
document.getElementsByTagName('head')[0].appendChild(script);
})
2) the interception code uses this repository as override of the XMLHttpRequest object, that I've tweaked a little bit as well in order to attach the method, url and sent data to it in order to be easily available when the response get's back.
Basically, I've overriden the open() method of the XMLHttpsRequest to attach those values that I might need in my script, and added the sentData in the send() method as well:
var RealXHROpen = XMLHttpRequest.prototype.open;
...
// Override open method of all XHR requests (inside wire() method
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
this.method = method;
this.url = url;
RealXHROpen.apply(this, arguments);
}
...
// Override send method of all XHR requests
XMLHttpRequest.prototype.send = function(sentData) {
...
this.sentData = sentData;
...
}
Then, I've added a callback on the response, which get's a modified XMLHttpRequest object WHEN the data comes back, and cotains everything: url, method, sentData and responseText with the retrieved data:
AjaxInterceptor.addResponseCallback(function(xhr) {
console.debug("response",xhr);
// xhr.method - contains the method used by the call
// xhr.url - contains the URL was sent to
// xhr.sentData - contains all the sent data (if any)
// xhr.responseText - contains the data sent back to the request
// Send XHR object back to injected script using DOM events
var event = new CustomEvent("ajaxResponse", {
detail: xhr
});
window.dispatchEvent(event);
});
AjaxInterceptor.wire();
For sending the XHR object from the intercept script back to the injected script, I just had to use DOM events like #Xan has suggested (thanks for that):
window.addEventListener("ajaxResponse", function(evt) {
console.debug('xhr response', evt.detail);
// do whatever you want with the XHR details ...
}, false);
Some extra hints / (workflow) optimisations that I've used in my project:
I've cleaned the GET url's and moved all the parameters (? &) into the dataSent property;
I've merged this dataSent property if there's the case (in send(data) method)
I've added an identifier on request send (timestamp) in order to match it later (see point bellow and get the idea);
I've sent a custom event to the script called "ajaxRequest" in order to prepare / optimise load times (I had to request some other data to some external API using CORS - by passing the call / response back and forth to the global.html which is capable of handling CORS), so I didn't had to wait for the original request to come back before sending my API call, but just matching the responses based on timestamp above;
When an jsf ajax is being redirected, the client receives a status code of success, responseCode of 200 and responseXML of null. is there not a way for client to know that it is a redirect?
In normal circumstances, the responseXML should definitely not be null. Given that, you could check as follows whether the current JSF ajax response represents a redirect and also where it would point to.
jsf.ajax.addOnEvent(function(data) {
if (data.status == "complete") {
var redirect = data.responseXML.getElementsByTagName("redirect");
if (redirect.length) {
var url = redirect[0].getAttribute("url");
// ...
}
}
});
I have an http module where I'm adding a response filter below for compression. This works for all API calls except for 1, the call to MetaData. If I remove the [BreezeController] decoration it works fine. I think it has to do with action filter attribute that converts the string return type into an HttpResponse return type with string content.
The error I'm getting is " Exception message: The stream state of the underlying compression routine is inconsistent."
I've done some testing where a method thats defined to return an HttpResponse works fine. So I think its the scenario where the method is defined to return string, and then the action filter changes it to HttpResponse at runtime.
Any ideas how I can get this to work?
Here's the response filter being added in BeginRequest:
HttpApplication app = (HttpApplication)sender;
// Check the header to see if it can accept compressed output
string encodings = app.Request.Headers.Get("Accept-Encoding");
if (encodings == null)
return;
Stream s = app.Response.Filter;
encodings = encodings.ToLower();
if (encodings.Contains("gzip"))
{
app.Response.Filter = new GZipStream(s, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
Don't know the specifics of what you're doing but I know that the [BreezeController] attribute strips out filters and adds back just the ones that breeze wants.
One approach might be to define a separate controller (ModelMetadataController) that only serves the metadata. This controller doesn't have the [BreezeController] attribute; it's a plain old Web API controller.
Then you create a "Breeze controller" (ModelController) with all of the usual methods except the Metadata method.
You call the metadata controller from the client during app launch via MetadataStore.fetchMetadata just to get metadata.
Once you have populated a metadataStore in this fashion, you use it in your EntityManager which sends query and save requests to the "real" Web API data controller.
The client code might look something like this:
var ds = new breeze.DataService({
serviceName: 'breeze/Model' // the breeze query & save controller
});
var ms = new MetadataStore({
namingConvention: breeze.NamingConvention.camelCase, // assuming that's what you want
});
ms.addDataService(ds); // associate the metadata-to-come with the "real" dataService
var manager = new breeze.EntityManager({
dataService: ds,
metadataStore: ms
});
// the fun bit: fetch the metadata from a different controller
var promise = ms.fetchMetadata('breeze/ModelMetadata') // the metadata-only controller!
return promise; // wait on it appropriately
I have searched in this forum for quiet a bit and here's my problem -
I have a ng-repeat in my html which takes in a list of messages(Json object).
Each message JSON has a sender email address - e.g. abc#gmail.com
Now, I have to get the email address from the message and form another REST API request to fetch their images for e.g. - http://<>:8080/getImage/abc#gmail.com (email address dynamic)
So in my code, I'll have a ng-repeat and a ng-src pointing to the image REST URL
If there's no image in server, it returns a 404 and displays a broken image on the UI. How do I handle it? On the other hand, if I make a http request to determine if there's a success message and on failure return a default image, then the whole thing goes through an endless loop. I'll try to create a fiddle and include it for better explanation.
Use the error block to handle such behavior:
function($http) {
var restUrl = 'getImage/abc';
return {
fetchImage: function(imageId) {
var self = this;
return $http.get(restUrl + '/' + imageId).
success(function(data) {
return self.imageUrl = data;
}).
error(function(data) {
return self.imageUrl = "pathToDefaultImage";
});
},
...
We've discovered a strange new bug in a GWT application I'm maintaining, and I'm not sure when it became an issue. Possibly with a new Firefox version.
We're sending a POST request to the server using a FormPanel, essentially like many examples I've seen online. But since we actually want a PUT request, one of the hidden input parameters is named "method" and has a value of "put".
Now, when I look at the request in Fiddler coming from Firefox, it is being transformed into a GET request with all the parameters in the QueryString. In IE and Chrome, the parameters are in the body of a POST request.
I've displayed the value of FormPanel.getMethod() in an alert, and in IE and Chrome the string "post" is displayed, whereas in firefox it is showing "object HTMLInputElement". Unfortunately, hosted mode debugging does not work with this project.
It obviously looks like the FormPanel's getMethod() function is returning the hidden input parameter named method instead of the actual form's method in Firefox.
Technically I should avoid changing the servlet as this is from an OpenSource project that we use, though I've found I can fix the issue by changing the hidden input parameter's name to "_method" on both ends.
Has anyone ever seen anything like this? I can't find anything in Google.
UPDATE: We're using GWT 2.3 in case that helps
Some insight can be found here Are the PUT, DELETE, HEAD, etc methods available in most web browsers?
I would also suggest using XMLHttpRequest. In this case you [most probably] don't have to change anything on the server side.
In case if you use Submit button, you can write in its clickHandler function:
submitMyForm(yourTextBox.getText(), self);
// self - is the instance of main class (named UploadForm here), needs to be passed here for future reference
and then some more (you can adapt this for your needs):
private native void submitMyForm(String text, UploadForm handler)/*-{
var fd = new FormData();
fd.append("textValue", text);
var xhr = new XMLHttpRequest();
var upload = xhr.upload;
readyStateChangeHandler = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var serverResponse = eval(xhr.responseText); // optional
handler.#com.project.UploadForm::onUploadIsDone(Lcom/google/gwt/core/client/JavaScriptObject;)(serverResponse);
} else {
handler.#com.project.UploadForm::onUploadFailed(I)(status);
}
}
};
xhr.onreadystatechange = readyStateChangeHandler;
xhr.open("PUT", yourActionUrlHere);
xhr.send(formData);
}-*/;