I'm trying to fire a plugin request from my ICN plugin. The request goes as below. However, I'm getting a 403 Forbidden error from the server.
Forbidden
You don't have permission to access /navigator/jaxrs/plugin on this server.
https://<icnserver.com>/navigator/jaxrs/plugin?repositoryId=Demo&query=%5B%7B%22name%22%3A%22ID%22%2C%22operator%22%3A%22LIKE%22%2C%22values%22%3A%5B%22123434234%22%2C%22%22%5D%7D%5D&className=Checks&plugin=DemoPlugin&action=DemoService&desktop=Demo
Plugin JS:
aspect.around(ecm.model.SearchTemplate.prototype, "_searchCompleted", function advisingFunction(original_searchCompleted){
return function(response, callback, teamspace){
var args = [];
var templateName = response.templates[0].template_name;
var res = response;
var requestParams = {};
requestParams.repositoryId = this.repository.id;
requestParams.query = query;
requestParams.className = templateName;
Request.invokePluginService("DemoPlugin", "DemoService",
{
requestParams: requestParams,
requestCompleteCallback: lang.hitch(this, function(resp) { // success
res.rows = resp.rows;
res.num_results = resp.rows.length;
res.totalCount = resp.rows.length;
args.push(res);
args.push(callback);
args.push(teamspace);
original_searchCompleted.apply(this,args);
})
}
);
}
});
You need to provide a security_token to be able to call your service, so you need to login first.
Then, open your browser's debug and check the requests in the network tab.
There you can see that every request that targets the /navigator/jaxrs/* URI will contain it, so something like this will be among the headers:
security_token: 163594541620199174
So my bet is that you have not set it in your client (I recommend a postman to test your service, or I would add a test (ICN) feature page in the ICN plugin project in order to be able to call it properly). In your feature page, you can call your service directly using the ecm/model/Request OOTB navigator dojo/javascript class, as you can see in CheckinAction.js:
_checkInDocument: function (item, requestData)
{
var self = this;
var payLoadObject = {requestType: "Get", id: item.id};
Request.postPluginService("DocuSignPlugin", "UpdateSignedDocumentService", "application/json", {
requestParams: {
repositoryId : item.repository.id,
serverType : item.repository.type,
docId : item.docid,
envelopeId: item.attributes.DSEnvelopeID,
payLoad: json.stringify(payLoadObject)
},
requestCompleteCallback: function(response) {
if (response.returncode == 0)
{
item.attributeDisplayValues.DSSignatureStatus = "Checkedin";
item.attributes.DSSignatureStatus = 4;
item.update(item);
}
else if (response.returncode == -1)
{
items = [];
items.push(item);
self._showLoginDialog(items);
}
},
backgroundRequest : false,
requestFailedCallback: function(errorResponse) {
// ignore handline failures
}
});
},
As you can see, you don't have to add the security_token to the requestParams, the framework will do it for you.
I am facing problem with my code in FireFox and Safari as below:
xhr = new window['XMLHttpRequest'];
xhr.onreadystatechange = function() {
if (done || xhr.readyState != 4) {
return;
}
done = true;
handleResponse(xhr.responseText, callback);
};
}
xhr.open('GET', uri+params, true);
xhr.withCredentials = true;
xhr.send(null);
function handleResponse(responseText, callback) {
var error;
var result;
try {
result = toucan.JSON.parse(responseText)['result']; //connectedAuth
logout result.
} catch (ex) {
result = undefined;
}
console.log("Result is" + result);
if (!result) {
var errorCode = 'UnknownError';
var errorMessage = 'An unknown error ocurred';
error = toucan.Base.format('%s: %s', errorCode, errorMessage);
}
invokeCallback(error, callback);
}
This is followed by redirection as :window.location.href = "index.php?module=login&method=logout";
However, I am not getting any response back from the request I made if it is followed by redirection in FireFox.
This works fine in Chrome but not in Firefox and is specific to the case when request is followed by redirection.
I do not have control on redirection code to be changed. Is there a way that the browser can be enforced to first complete the request and get the response before going for redirection while keeping the call asynchronous.
I would suggest you to use a promise, first create a function that run the ajax call that return the response from your server:
ajax_AuthUser(id,pass){
return $.ajax({
method: "POST",
url: "authUser.php",
data: { id: id, pass: pass}
})
}
Second use a done statement:
ajax_AuthUser(id,pass)
.done(function(response){
//check the response here !! maybe validate the json ?
var auth = JSON.parse(response)
if(auth.response == "approved"){
//do something here
}else{
//do other stuff here
}
}).fail(function(response){
//do something if fail
}).always(function(){
//do something after the call finished
})
If you want a live example here is a jsfiddle that show how promises work
Hope it helps
I need to download a file from the server when a button is clicked.
I created a MaterialUI button and on its onclick callback i call an action of the container component connected.
The action is asynchronous and does an ajax POST:
export const onXlsxClick = () => dispatch => {
const urlParams = {
filters: {
aggregation: 'macro_area',
chart_resolution: '1_hour',
chart_from: '1478080363',
chart_to: '1477993963'
},
labels: ['PROVA1', 'PROVA2'],
series: [
{
label: null,
timestamp: 1478080363,
values: [123, 345]
},
{
label: null,
timestamp: 1477993963,
values: [153, 3435]
}
]
};
return $.ajax({
url:'/rest/export/chart/xlsx',
type: 'POST',
dataType: 'application/json',
contentType: 'application/json',
data: JSON.stringify(urlParams)
})
.done(data => {
console.log('success');
})
.fail(error => {
console.log(error);
});
};
The server receive the request and handle it correctly through this REST service:
#POST
#Path("xlsx")
#Produces("application/vnd.ms-excel")
public Response getXlsx(ChartExportRequest request) {
ResponseBuilder responseBuilder;
ChartExportRequestDTO reqDto = null;
try {
reqDto = parseDTO(request);
checkRequestDTO(reqDto);
ExportDTO dto = getXlsxProvider().create(reqDto);
responseBuilder = Response.ok(dto.getFile())
.header("Content-disposition", "attachment;filename=" + dto.getFileName());
}
catch(Exception e) {
logger.error("Error providing export xlsx for tab RIGEDI with request [" + (reqDto != null ? reqDto.toString() : null) + "]", e);
responseBuilder = Response.serverError().entity(e.getMessage());
}
return responseBuilder.build();
}
The problem is that the response arrives correctly to the client but then nothing happens: I am expecting that the browser shows the download dialog (example: in chrome I expect the bottom bar of downloads to appear with my file).
What am I doing wrong?
AS per Nate's answer here, the response of Ajax request is not recognised by a browser as a file. It will behave in the same way for all Ajax responses.
You need to trigger the download popup manually.
In my implementation, I used filesaverjs to trigger the download popup, once I have received the API response in reducer.
Since FileSaver uses blob for saving the file, I am sending the response from server as a blob, converting it into string array buffer and then using it to save my file. This approach was described in
Please find the sample code below for the reducer :
(using reducer for state modification, as per Redux)
reducer.js
let fileSaver = require("file-saver");
export default function projectReducer(state = {}, action)
{
let project;
switch (action.type) {
case GET_PROJECT_SUCCESS :
project = Object.assign(action.response.data);
return project;
case EXPORT_AND_DOWNLOAD_DATA_SUCCESS :
let data = s2ab(action.response.data);
fileSaver.saveAs(new Blob([data], {type: "application/octet-stream"}), "test.xlsx");
return state;
}
return state;
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
The data on the webpage is displayed dynamically and it seems that checking for every change in the html and extracting the data is a very daunting task and also needs me to use very unreliable XPaths. So I would want to be able to extract the data from the XHR packets.
I hope to be able to extract information from XHR packets as well as generate 'XHR' packets to be sent to the server.
The extracting information part is more important for me because the sending of information can be handled easily by automatically triggering html elements using casperjs.
I'm attaching a screenshot of what I mean.
The text in the response tab is the data I need to process afterwards. (This XHR response has been received from the server.)
This is not easily possible, because the resource.received event handler only provides meta data like url, headers or status, but not the actual data. The underlying phantomjs event handler acts the same way.
Stateless AJAX Request
If the ajax call is stateless, you may repeat the request
casper.on("resource.received", function(resource){
// somehow identify this request, here: if it contains ".json"
// it also also only does something when the stage is "end" otherwise this would be executed two times
if (resource.url.indexOf(".json") != -1 && resource.stage == "end") {
var data = casper.evaluate(function(url){
// synchronous GET request
return __utils__.sendAJAX(url, "GET");
}, resource.url);
// do something with data, you might need to JSON.parse(data)
}
});
casper.start(url); // your script
You may want to add the event listener to resource.requested. That way you don't need to way for the call to complete.
You can also do this right inside of the control flow like this (source: A: CasperJS waitForResource: how to get the resource i've waited for):
casper.start(url);
var res, resData;
casper.waitForResource(function check(resource){
res = resource;
return resource.url.indexOf(".json") != -1;
}, function then(){
resData = casper.evaluate(function(url){
// synchronous GET request
return __utils__.sendAJAX(url, "GET");
}, res.url);
// do something with the data here or in a later step
});
casper.run();
Stateful AJAX Request
If it is not stateless, you would need to replace the implementation of XMLHttpRequest. You will need to inject your own implementation of the onreadystatechange handler, collect the information in the page window object and later collect it in another evaluate call.
You may want to look at the XHR faker in sinon.js or use the following complete proxy for XMLHttpRequest (I modeled it after method 3 from How can I create a XMLHttpRequest wrapper/proxy?):
function replaceXHR(){
(function(window, debug){
function args(a){
var s = "";
for(var i = 0; i < a.length; i++) {
s += "\t\n[" + i + "] => " + a[i];
}
return s;
}
var _XMLHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
this.xhr = new _XMLHttpRequest();
}
// proxy ALL methods/properties
var methods = [
"open",
"abort",
"setRequestHeader",
"send",
"addEventListener",
"removeEventListener",
"getResponseHeader",
"getAllResponseHeaders",
"dispatchEvent",
"overrideMimeType"
];
methods.forEach(function(method){
window.XMLHttpRequest.prototype[method] = function() {
if (debug) console.log("ARGUMENTS", method, args(arguments));
if (method == "open") {
this._url = arguments[1];
}
return this.xhr[method].apply(this.xhr, arguments);
}
});
// proxy change event handler
Object.defineProperty(window.XMLHttpRequest.prototype, "onreadystatechange", {
get: function(){
// this will probably never called
return this.xhr.onreadystatechange;
},
set: function(onreadystatechange){
var that = this.xhr;
var realThis = this;
that.onreadystatechange = function(){
// request is fully loaded
if (that.readyState == 4) {
if (debug) console.log("RESPONSE RECEIVED:", typeof that.responseText == "string" ? that.responseText.length : "none");
// there is a response and filter execution based on url
if (that.responseText && realThis._url.indexOf("whatever") != -1) {
window.myAwesomeResponse = that.responseText;
}
}
onreadystatechange.call(that);
};
}
});
var otherscalars = [
"onabort",
"onerror",
"onload",
"onloadstart",
"onloadend",
"onprogress",
"readyState",
"responseText",
"responseType",
"responseXML",
"status",
"statusText",
"upload",
"withCredentials",
"DONE",
"UNSENT",
"HEADERS_RECEIVED",
"LOADING",
"OPENED"
];
otherscalars.forEach(function(scalar){
Object.defineProperty(window.XMLHttpRequest.prototype, scalar, {
get: function(){
return this.xhr[scalar];
},
set: function(obj){
this.xhr[scalar] = obj;
}
});
});
})(window, false);
}
If you want to capture the AJAX calls from the very beginning, you need to add this to one of the first event handlers
casper.on("page.initialized", function(resource){
this.evaluate(replaceXHR);
});
or evaluate(replaceXHR) when you need it.
The control flow would look like this:
function replaceXHR(){ /* from above*/ }
casper.start(yourUrl, function(){
this.evaluate(replaceXHR);
});
function getAwesomeResponse(){
return this.evaluate(function(){
return window.myAwesomeResponse;
});
}
// stops waiting if window.myAwesomeResponse is something that evaluates to true
casper.waitFor(getAwesomeResponse, function then(){
var data = JSON.parse(getAwesomeResponse());
// Do something with data
});
casper.run();
As described above, I create a proxy for XMLHttpRequest so that every time it is used on the page, I can do something with it. The page that you scrape uses the xhr.onreadystatechange callback to receive data. The proxying is done by defining a specific setter function which writes the received data to window.myAwesomeResponse in the page context. The only thing you need to do is retrieving this text.
JSONP Request
Writing a proxy for JSONP is even easier, if you know the prefix (the function to call with the loaded JSON e.g. insert({"data":["Some", "JSON", "here"],"id":"asdasda")). You can overwrite insert in the page context
after the page is loaded
casper.start(url).then(function(){
this.evaluate(function(){
var oldInsert = insert;
insert = function(json){
window.myAwesomeResponse = json;
oldInsert.apply(window, arguments);
};
});
}).waitFor(getAwesomeResponse, function then(){
var data = JSON.parse(getAwesomeResponse());
// Do something with data
}).run();
or before the request is received (if the function is registered just before the request is invoked)
casper.on("resource.requested", function(resource){
// filter on the correct call
if (resource.url.indexOf(".jsonp") != -1) {
this.evaluate(function(){
var oldInsert = insert;
insert = function(json){
window.myAwesomeResponse = json;
oldInsert.apply(window, arguments);
};
});
}
}).run();
casper.start(url).waitFor(getAwesomeResponse, function then(){
var data = JSON.parse(getAwesomeResponse());
// Do something with data
}).run();
I may be late into the party, but the answer may help someone like me who would fall into this problem later in future.
I had to start with PhantomJS, then moved to CasperJS but finally settled with SlimerJS. Slimer is based on Phantom, is compatible with Casper, and can send you back the response body using the same onResponseReceived method, in "response.body" part.
Reference: https://docs.slimerjs.org/current/api/webpage.html#webpage-onresourcereceived
#Artjom's answer's doesn't work for me in the recent Chrome and CasperJS versions.
Based on #Artjom's answer and based on gilly3's answer on how to replace XMLHttpRequest, I have composed a new solution that should work in most/all versions of the different browsers. Works for me.
SlimerJS cannot work on newer version of FireFox, therefore no good for me.
Here is the the generic code to add a listner to load of XHR (not dependent on CasperJS):
var addXHRListener = function (XHROnStateChange) {
var XHROnLoad = function () {
if (this.readyState == 4) {
XHROnStateChange(this)
}
}
var open_original = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, unk1, unk2) {
this.requestUrl = url
open_original.apply(this, arguments);
};
var xhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
var xhr = this;
if (xhr.addEventListener) {
xhr.removeEventListener("readystatechange", XHROnLoad);
xhr.addEventListener("readystatechange", XHROnLoad, false);
} else {
function readyStateChange() {
if (handler) {
if (handler.handleEvent) {
handler.handleEvent.apply(xhr, arguments);
} else {
handler.apply(xhr, arguments);
}
}
XHROnLoad.apply(xhr, arguments);
setReadyStateChange();
}
function setReadyStateChange() {
setTimeout(function () {
if (xhr.onreadystatechange != readyStateChange) {
handler = xhr.onreadystatechange;
xhr.onreadystatechange = readyStateChange;
}
}, 1);
}
var handler;
setReadyStateChange();
}
xhrSend.apply(xhr, arguments);
};
}
Here is CasperJS code to emit a custom event on load of XHR:
casper.on("page.initialized", function (resource) {
var emitXHRLoad = function (xhr) {
window.callPhantom({eventName: 'xhr.load', eventData: xhr})
}
this.evaluate(addXHRListener, emitXHRLoad);
});
casper.on('remote.callback', function (data) {
casper.emit(data.eventName, data.eventData)
});
Here is a code to listen to "xhr.load" event and get the XHR response body:
casper.on('xhr.load', function (xhr) {
console.log('xhr load', xhr.requestUrl)
console.log('xhr load', xhr.responseText)
});
Additionally, you can also directly download the content and manipulate it later.
Here is the example of the script I am using to retrieve a JSON and save it locally :
var casper = require('casper').create({
pageSettings: {
webSecurityEnabled: false
}
});
var url = 'https://twitter.com/users/username_available?username=whatever';
casper.start('about:blank', function() {
this.download(url, "hop.json");
});
casper.run(function() {
this.echo('Done.').exit();
});
I am getting HTTP error 405 verb not allowed. As sometimes code works and sometimes throws http 405 error, I need to understand whether this is programming problem or server configuration problem. I am using ajax with jquery. I have gone through all related posts here and tried all recommended options related with the code. Please help.
my javascript code is as follows
$(function() {
$('.error').hide();
$(".button").click(function() {
// validate and process form
// first hide any error messages
$('.error').hide();
var name = $("input#name").val();
if (name == "") {
$("label#name_error").show();
$("input#name").focus();
return false;
}
var email = $("input#email").val();
if (email == "") {
$("label#name_error").show();
$("input#email").focus();
return false;
}
var textquery = $("textarea#textquery").val();
if (textquery == "") {
$("label#name_error").show();
$("textarea#textquery").focus();
return false;
}
var dataString = name + email + textquery;
// alert (dataString);return false;
$.ajax({
type: "POST",
url: "samplemail.aspx",
data: dataString,
success: function() {
$('#contact_form').html("<div id='message'></div>");
$('#message').html("<h2>Contact Form <br> Submitted!</h2>")
.append("<p>We will be in touch soon.</p>")
.hide()
.fadeIn(1500, function() {
$('#message').append("<img id='checkmark' src='images/check.png' />");
});
}
});
return false;
});
});
runOnLoad(function(){
$("input#name").select().focus();
});
Problem solved
the way Of passing parameter was wrong i.e.data : datastring .
The correct way is data : { name : name, email: email, textquery: textquery}