I want to test how my web application deals with server responses. That's why I created a test scenario which uses Sinon.JS to fake a server.
My application code makes two requests and in my test scenario I want to force the situation that the response to the first request is sent after the response to my second request.
Sequence:
Request 1
Request 2
Response 2
Response 1
Here is the CoffeeScript code that I wrote for my test case:
# Request 1
server.respondWith 'GET', "http://localhost/endpoint", [200, {"Content-Type": "application/json"}, '{"A":"A"}']
# Request 2
server.respondWith 'GET', "http://localhost/endpoint", [200, {"Content-Type": "application/json"}, '{"B":"B"}']
# My application code
...
# Response 1
server.respond()
# Response 2
server.respond()
Once I start the test all REST calls to http://localhost/endpoint from my application code get the same response ({"B":"B"}). So to me it looks like Sinon.JS takes always the response from the last URL mapping which has been defined using respondWith.
But I want my faked server to return {"B":"B"} to the first hit on http://localhost/endpoint. On the second hit it should return {"A":"A"}.
Is it possible to do something like this?
# Request 1
request_1 = server.respondWith 'GET', "http://localhost/endpoint", [200, {"Content-Type": "application/json"}, '{"A":"A"}']
# Request 2
request_2 = server.respondWith 'GET', "http://localhost/endpoint", [200, {"Content-Type": "application/json"}, '{"B":"B"}']
# My application code (makes multiple requests to the same endpoint)
...
# Response 1
request_2.respond()
# Response 2
request_1.respond()
You can use the Jasmine-AJAX lib made by Pivotal for this.
CoffeeScript:
it 'can handle an unlimited amount of requests and respond to each one individually after all requests have been made', ->
jasmine.Ajax.install() # put this in beforeEach
url = 'http://localhost/test'
$.ajax
dataType: 'json'
url: url
success: (data, textStatus, jqXHR) ->
# Receives {"A":"A"}
console.log "Response: #{JSON.stringify(data)}"
$.ajax
dataType: 'json'
url: url
success: (data, textStatus, jqXHR) ->
# Receives {"B":"B"}
console.log "Response: #{JSON.stringify(data)}"
responses = [
{
contentType: 'application/json'
responseText: '{"A":"A"}'
status: 200
},
{
contentType: 'application/json'
responseText: '{"B":"B"}'
status: 200
}
]
for i in [0...jasmine.Ajax.requests.count()]
request = jasmine.Ajax.requests.at i
request.respondWith responses[i]
expect(jasmine.Ajax.requests.count()).toBe 2
jasmine.Ajax.uninstall() # put this in afterEach
With count() and at() you can get all requests ordered by time and put them in an array where you can for example shift the requests and respond to them.
var count = 0
$.mockjax(
function(requestSettings){
if(requestSettings.url === "path/to/api" && requestSettings.type === "POST"){
return {
response: function(origSettings){
if (count == 0){
this.responseText = {msg:"this is first response" }
this.status = 200
}
else if(count == 1){
//more combos
}
else{
this.responseText = {msg:"this is last response" }
this.status = 200
}
count = (count + 1) % 4
}
}
}
}
})
Related
I have a Flask server running in Apache that returns an HTML page with a button to submit an AJAX POST request to a relative URL. In Firefox, the AJAX call fails immediately with a 400, logging POST response ERROR code: 400. Logging the xhr object or the data parameter to the console results in <unavailable>, with its responseText attribute as undefined. The response is exactly the same, regardless of what URL is passed to AJAX. The absolute URL (http://thehostname.com/path/send-email) also gives the same result.
Apache's access.log shows no request coming in at the time of the AJAX requests. The request does not come up at all in Firefox's "Network" tab of the dev tools. The request works fine in Chrome with all other conditions the same, as well is with cURL.
The code is below. Please help!
HTML:
<img width="20px" src="mail.png" alt="" onerror="this.style.display='none'" onclick="sendEmail('val1', 'val2', 'val3')">
JS:
function sendEmail(val1, val2, val3) {
val1 = prompt('Prompt2 for ' + val2 + ':', val1);
console.log('Val1: '+ val1);
if(val1) {
let xhr = $.ajax({
method: 'POST',
url: 'send-email',
// Tried with and without stringify
data: JSON.stringify({param1: val1, param2: val2, param3: val3}),
contentType: "application/json; charset=utf-8",
dataType: 'text',
cache: false,
traditional: true
}).done(function(){
alert("Sent!");
}).fail(function(data){
console.log("Info:");
console.log(xhr.responseText);
alert('Failed to send');
});
}
console.log("Ret false");
return false;
};
Python endpoint:
#app.route('/autorack_testing_report/send-email', methods=['POST'])
#app.route('/send-email', methods=['POST'])
def send_email():
print("Send email") # Not getting printed
info = request.form or request.json
if not (info and info.get('param1') and info.get('param2')):
abort(400, 'Must supply recipient and ticket')
send_to_list = re.split(',|;', info['param1'])
send_to = []
for s in send_to_list:
if s.strip():
send_to.append(s.strip() + '#domain.com')
send_to = ','.join(send_to)
ticket = info['ticket']
smtp = smtplib.SMTP('mysmtphost', 25)
msg = MIMEMultipart('alternative')
msg['Subject'] = 'subject'
msg['From'] = msg['To'] = send_to
msg.attach(MIMEText('somecontent', 'plain'))
smtp.sendmail(send_to, send_to, msg.as_string())
smtp.quit()
resp = make_response('Success', 200)
resp.headers['Content-Type'] = 'text/plain'
return resp
Firefox console output:
Val1: myinput
Ret false
POST response ERROR code: 400
POST response ERROR code: 400
Info:
undefined
No Python output, since Apache isn't receiving the request.
The 400 Bad Request response status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing). where does 'send-email' point to?
I am trying to post a object array, I expect the post to post JSON like so
{"campaign":"ben",
"slots":[
{
"base_image": "base64 code here"
}
]
}
When I post I get this in the console
angular.js:9866 POST /ccuploader/Campaign/updateSlots 413 (Payload Too Large)
(anonymous) # angular.js:9866
n # angular.js:9667
f # angular.js:9383
(anonymous) # angular.js:13248
$eval # angular.js:14466
$digest # angular.js:14282
$apply # angular.js:14571
(anonymous) # angular.js:21571
dispatch # jquery.min.js:3
r.handle # jquery.min.js:3
app.js:71 failed
Im not sure why my post is not working. Can someone point out my mistake ?
JavaScript code
$scope.SaveImage = function () {
$http({
url: "http://www.somesite.co.uk/ccuploader/Campaign/updateSlots",
method: "POST",
data: $.param({ 'campaign': "name", 'slots': $scope.slots }),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function (response) {
// success
console.log('success');
console.log("then : " + JSON.stringify(response));
}, function (response) { // optional
// failed
console.log('failed');
console.log(JSON.stringify(response));
});
};
Seems you are sending a base64 string on the POST request.
Most web servers have a max POST limit.
You should configure your server to allow large POST params.
Implementation is different from server to server.
If your server is using PHP refer this.
Increasing the maximum post size
Also it is better if you can upload images by chunking them. There are lot of libraries that does it. Otherwise your browser will hang and the request will eventually timeout. That's called the multipart upload.
You can upload GBs of images without no problem with multipart upload mechanism.
UPDATE
Also without using the $.param function just pass the parameters directly to the data object. Since the payload is heavy the $.param may throw this exception when it's trying to parse the request.
$scope.SaveImage = function () {
$http({
url: "http://www.somesite.co.uk/ccuploader/Campaign/updateSlots",
method: "POST",
data: {
campaign: "name",
slots: $scope.slots
}),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function (response) {
// success
console.log('success');
console.log("then : " + JSON.stringify(response));
}, function (response) { // optional
// failed
console.log('failed');
console.log(JSON.stringify(response));
});
I'm having some issues with the "Cross-Origin Request Blocked". I tried to allow first from the server, than from all ("*"). Got every time the same error message on the chrome developer toolkit.
Here is my flask python code:
application = Flask(__name__)
application.config.from_object(__name__)
cors = CORS(application, resorces={r'/*': {"origins": '*'}})
#application.route("/get-live-data",methods=['GET'])
#cross_origin()
def live_data():
con = connect_db()
cur = con.cursor()
cur.execute("SELECT * from envoiContinuT")
sqlite_result = cur.fetchall()
cle = json.load(open(JSON_STATUS))
parametres = json.load(open(JSON_PARAMETRES))
descT = []
for key in cle["status"]:
attr = parametres[key]
if attr["envoiC"] == 1:
descT.append(attr["description"])
response = any_response(flask.jsonify(data=descT))
return response
Here is my Ajax code:
var baseURL = "http://localhost:8000";
function getLiveData(data){
//Get the parameters descriptions
$.ajax({
method: 'GET',
url:baseURL + '/get-live-data',
headers: {
"Accept" : "application/json",
"Content-type": "application/json"
},
success:function(data){
console.log(data);
//populateAccordion(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("Status: " + textStatus);
console.log("Error: " + errorThrown);
}
});
}
Thank you for your answer!
You have a typo here:
cors = CORS(application, resorces={r'/*': {"origins": '*'}})
^^^^^^^^
Should be:
cors = CORS(application, resources={r'/*': {"origins": '*'}})
Also, there’s no point in sending a Content-type request header for GET request. There’s no request body for GET requests, so no need to specify a content type. So instead just do this:
headers: {
"Accept" : "application/json",
},
Otherwise, if you send a Content-Type request header with the value application/json, that triggers your browser to do a CORS preflight OPTIONS request, and your config must allow it:
#application.route("/get-live-data",methods=['GET', 'POST'])
#cross_origin(headers=['Content-Type']) # Send Access-Control-Allow-Headers
But if you allow the Content-Type request header, you might as well allow POST requests too (as above)—since as mentioned earlier here, there’s no point in allowing it just for GET requests.
So I'm working on posting a video to the Emotion API for video and I haven't been able to get a response.
I've been able to get it to work on the Microsoft online console, but when I try to implement it in my Rails app using (1) JavaScript Ajax, or (2) Ruby server-side code, I consistently get various errors.
Here's my code. At first I tried to Ajax way, but I had a suspicion that the API doesn't have CORS enabled. So then I tried Ruby, to no success.
Ruby attempt:
def index
uri = URI('https://api.projectoxford.ai/emotion/v1.0/recognizeinvideo')
uri.query = URI.encode_www_form({
})
data = File.read("./public/mark_zuck.mov")
request = Net::HTTP::Post.new(uri.request_uri)
# Request headers
request['Ocp-Apim-Subscription-Key'] = 'e0ae8aad4c7f4e33b51d776730cff5a9'
# Request body
request.body = data
request.content_type = "video/mov"
response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
http.request(request)
end
puts response.body
end
Here's my Ajax attempt:
function CallAPI(apiUrl, apiKey){
console.log("API called");
$(".loading").css("display", "inline-block");
$.ajax({
url: apiUrl,
beforeSend: function (xhrObj) {
xhrObj.setRequestHeader("Content-Type", "application/octet-stream");
xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", apiKey);
},
type: "POST",
data: '{"url": "http://localhost:5000/mark_zuck.mov"}',
processData: false,
success: function(response){
console.log("API success");
ProcessResult(response);
$(".loading").css("display", "none");
console.log(response);
},
error: function(error){
console.log("API failed");
$("#response").text(error.getAllResponseHeaders());
$(".loading").css("display", "none");
console.log(error);
}
})
Yes, I've regenerated my key. This is just to illustrate my point.
So you have to set Content-Type to application/octet-stream if it's a binary file you're sending, like I was.
If you use a url you should set Content-Type to application/json and the url must be publicly available.
At start of my app I need to send three ajax get (Dojo xhrGET ) requests, but problem is that I need to send second when I process data from first and send third when I process data from second ( order is important ! ). I put this requests one behind other but it sometimes doesn't work. How to synchronize and solve this, is there any way to lock or wait like in Java ?
If you're using 1.6, check out the new Promises API (returned by dojo.xhrGet), or use deferreds in 1.5. They provide a 'neater' way to achieve this.
Essentially you can write:
dojo.xhrGet({
url: './_data/states.json',
handleAs: 'json'
}).then(
function(response) {
// Response is the XHR response
console.log(response);
dojo.xhrGet({
url: './_data/'+response.identifier+'.json',
handleAs: 'json'
}).then(
function(response2) {
// The second XHR will fail
},
// Use the error function directly
errorFun
)
},
function(errResponse) {
// Create a function to handle the response
errorFun(err);
}
)
var errorFun = function(err) {
console.log(err);
}
See http://dojotoolkit.org/documentation/tutorials/1.6/deferreds/ and http://dojotoolkit.org/documentation/tutorials/1.6/promises/ for more information
You can use option sync = true, and put the request one behind other. With this, 3rd will be sent after 2nd after 1st.
Or you can begin send 2nd request after 1st is done by using load function.
Example:
dojo.xhrGet({ //1st request
load: function(){
dojo.xhrGet({ //2nd request
load: function(){
dojo.xhrGet({ //3nd request
});
}
});
}
});
For more information: http://dojotoolkit.org/reference-guide/dojo/xhrGet.html
Can we make the second ajax request in the success callback method of the first request:
$.ajax({
'type' : 'get', // change if needed
'dataType' : 'text', // data type you're expecting
'data' : { 'className' : divClass },
'url' : url,
'success' : function(newClass) {
//make the second ajax request...
}
});
And do the same thing for the third request.