I encountered a strange problem. In my app I have the following code
WinJS.xhr({
url: 'http://bdzservice.apphb.com/api/Route?fromStation=София&toStation=Варна&date=30/08/2013&startTime=00:00&endTime=24:00'
}).then(function (success)
{
console.log(success);
},
function (error)
{
console.log(error);
}
);
The problem is I get an empty response text (with status 200). The Url I provided returns data through the browser and other rest clients, but in the app I get no data. Where might be the problem?
You need to encode query string parameters via encodeURIComponent (browser does this for you automatically when pasting url).
Following code will do the trick:
function serialize (obj) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
};
var request = {
fromStation: 'София',
toStation: 'Варна',
date: '30/08/2013',
startTime: '00:00',
endTime: '24:00'
};
WinJS.xhr({
url: 'http://bdzservice.apphb.com/api/Route?' + serialize(request)
}).then(function(success) {
console.log(success);
},
function(error) {
console.log(error);
}
);
Related
I am using hapijs version 17.0.1. I am trying to upload an image using ajax request on a hapijs route. Here is my AJAX code to upload profile pic:
var image_file_input = document.getElementById("user_profile_upload");
image_file_input.onchange = function () {
if(this.files != undefined)
{
if(this.files[0] != undefined)
{
var formData = tests.formdata ? new FormData() : null;
if (tests.formdata)
{
//alert(file)
formData.append('image_file', this.files[0]);
formData.append('userId', user_id);
formData.append('memberId', member_id);
}
$.ajax({
url: "/v1/User/uploadUserPic",
data: formData,
type: "POST",
dataType: "json",
contentType: false,
processData: false,
contentType: "multipart/form-data",
success: function(data){
console.log(data);
var errMsg = null;
var resData = null;
if(data.statusCode == 200)
{
resData = data.result;
}
else
{
alert(data.message)
}
},
error: function(error){
alert(error);
}
});
}
}
}
And here is my Hapijs route Code:
var uploadUserPic = {
method: 'POST',
path: '/v1/Module/uploadUserPic',
config: {
description: 'Update Image For User',
tags: ['api', 'User'],
auth: 'session',
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
userId : Joi.string().regex(/^[a-f\d]{24}$/i).required(),
memberId: Joi.string().required(),
image_file: Joi.object().required(),
},
failAction: FailCallBack
}
},
handler: function (request, reply) {
var resultData = null;
var error = null;
return new Promise(function (resolve) {
var multiparty = require('multiparty');
var fs = require('fs');
var form = new multiparty.Form();
form.parse(request.payload, function (err, fields, files) {
if(err)
{
error = err;
resolve();
}
else
{
var mkdirp = require('mkdirp');
var img_dir = "./files/users/";
mkdirp(img_dir, function (err) {
if (err)
{
error = err;
console.error(err);
resolve();
}
else
{
var oldpath = files.image_file.path;
var newpath = "./files/users/"+requestPayload.userId+".png";
fs.rename(oldpath, newpath, function (err) {
if(err)
{
error = err;
}
resolve();
});
}
});
}
});
}).then(function (err, result) {
if(err) return sendError(err);
if(error) return sendError(error)
return {
"statusCode": 200,
"success": true
};
});
}
}
The above code gives me following error cannot read property 'content-length' of undefined on line form.parse(request.payload, function (err, fields, files) {});
Please let me know If I am doing something wrong. If I replace the url in ajax request with anohter url that I have written in php then it works perfectly. which means that something is wrong with my hapijs/nodejs code.
There's a good post on how to handle file uploads in Hapi.js (written in version 16) https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js
Since you are using payload.parse = true, I am not seeing a particular reason why you have to use multiparty. I have the following working code that would save files (of any type) uploaded from client into uploads directory on the server (Please do not use directly on production as no sanitation is done)
{
path: '/upload',
method: 'POST',
config: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
files: Joi.array().single()
}
}
},
handler: function(request) {
const p = request.payload, files = p.files
if(files) {
console.log(`${files.length} files`)
files.forEach(async file => {
const filename= file.hapi.filename
console.log(`Saving ${filename} to ./uploads`)
const out = fs.createWriteStream(`./uploads/${filename}`)
await file.pipe(out)
})
}
return {result: 'ok'}
}
}
You can use the following curl command to test
curl http://localhost:8080/upload -F 'files=#/path/to/a/note.txt' -F 'files=#/path/to/test.png' -vvv
There are a few issues with your code. First in your $.ajax call, you have specified contentType twice, although it's not a syntax error but it's careless to code like that. Second the function's signature inside your .then() block is incorrect. You are mixing the idea of Promise and callback. I don't think the following line will be triggered
if(err) return sendError(err);
One last trivial thing, you said you are using Hapi 17 but based on the handler function's signature
handler: function (request, reply) {
...
Seems you are not totally onboard with Hapi17 as the new signature is
handler: function (request, h) {
And it's not just the rename of reply to h.
I wrote two Ajax calls that request data from stored procedures (in SQL Server), sp_ahtreatmentselect and sp_inventoryselect. Here is how the functions look like in the Breeze controller.
[HttpPost]
[ActionName("getinventories")]
public object GetInventories(HttpRequestMessage request)
{
var data = request.Content.ReadAsFormDataAsync().Result;
var opId = data["operationid"];
string query = "sp_inventoryselect #operationId";
SqlParameter operationId = new SqlParameter("#operationId", opId);
return UnitOfWork.Context().ExecuteStoreQuery<GetInventories>(query, operationId);
}
[HttpPost]
[ActionName("gettreatments")]
public object GetTreatments(HttpRequestMessage request)
{
var data = request.Content.ReadAsFormDataAsync().Result;
var opId = data["operationid"];
string query = "sp_ahtreatmentselect #operationId";
SqlParameter operationId = new SqlParameter("#operationId", opId);
return UnitOfWork.Context().ExecuteStoreQuery<GetTreatments>(query, operationId);
}
Now, on client-side, the Ajax calls look like this:
var ajaxImpl = breeze.config.getAdapterInstance('ajax');
function treatments(id) {
return ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/gettreatments',
data: { operationid: id },
success: function(data) {
console.log('Success!');
},
error: function(error) {
console.log('Error!');
}
});
}
function inventories(id) {
return ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/getinventories',
data: { operationid: id },
success: function (data) {
console.log('Success!');
},
error: function(error) {
console.log('Error!');
}
});
}
return inventories(id).then(treatments(id))
.then(function() {
// Do something
})
.fail(function(error) {
// Display error
});
Both Ajax calls work fine, BUT problem is, // Do something is run BEFORE inventories(id) and treatments(id) are run. I would like it to work the other way around instead. I also tried $.when(inventories(id), treatments(id)).then(...) and $.when(ajaxImpl.ajax(...), ajaxImpl.ajax(...)).then(...), but same problem occurs. How do I solve this?
Thanks in advance.
I realized that the issue lay on synchronous calls, not Breeze. I used queue.js to make Ajax calls and // Do something and the calls are completed. Here is how.
var ajaxImpl = breeze.config.getAdapterInstance('ajax'),
serviceName = 'some/service',
id = 1; // Could be any number
return queue()
.defer(function(callback) {
ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/getinventories',
data: { operationid: id },
success: function (data) {
console.log('Success!');
callback(null, data);
});
})
.defer(function(callback) {
ajaxImpl.ajax({
type: 'POST',
url: serviceName + '/gettreatments',
data: { operationid: id },
success: function (data) {
console.log('Success!');
callback(null, data);
});
})
.awaitAll(function(error, results) {
if (error) {
console.log('Error! ' + error);
} else {
// Do something, given that:
// results[0] are inventories, and
// results[1] are treatments
}
});
I don't know what this has to do with Breeze. Did you get your ajaxImpl from a Breeze ajax adapter? I've looked at both ajax adapters shipped with Breeze and neither of them returns anything from a call to the ajax method!
Accordingly, you're expression should have died immediately with a reference error when it got to the .then in return inventories(id).then(.... I don't see how it could have gotten to \\ do something at all.
Something isn't right with the way you've posed this question.
Once you get beyond this I still don't see how this involves Breeze. Breeze won't do anything with the results of your service requests.
I'm completely flummoxed by your question.
I have been trying to do a specific operation once I receive the submitAdapterAuthentication from the challenge handler and I could not do any operation because my code it does not even compile through it. I am using the submitAdapterAuthentication in one method of my angular service. The method looks like this:
login: function (user, pass) {
//promise
var deferred = $q.defer();
//tempuser
tempUser = {username: user, password: pass};
userObj.user = user;
checkOnline().then(function (onl) {
if (onl) { //online
console.log("attempting online login");
var auth = "Basic " + window.btoa(user + ":" + pass);
var invocationData = {
parameters: [auth, user],
adapter: "SingleStepAuthAdapter",
procedure: "submitLogin"
};
ch.submitAdapterAuthentication(invocationData, {
onFailure: function (error) {
console.log("ERROR ON FAIL: ", error);
},
onConnectionFailure: function (error) {
console.log("BAD CONNECTION - OMAR", error);
},
timeout: 10000,
fromChallengeRequest: true,
onSuccess: function () {
console.log("-> submitAdapterAuthentication onSuccess!");
//update user info, as somehow isUserAuthenticated return false without it
WL.Client.updateUserInfo({
onSuccess: function () {
//return promise
deferred.resolve(true);
}
});
}
});
} else { //offline
console.log("attempting offline login");
deferred.resolve(offlineLogin());
}
uiService.hideBusyIndicator();
});
uiService.hideBusyIndicator();
return deferred.promise;
}
where ch is
var ch = WL.Client.createChallengeHandler(securityTest);
and checkOnline is this function that checks whether the user is online or not:
function checkOnline() {
var deferred = $q.defer();
WL.Client.connect({
onSuccess: function () {
console.log("** User is online!");
deferred.resolve(true);
},
onFailure: function () {
console.log("** User is offline!");
deferred.resolve(false);
},
timeout: 1000
});
return deferred.promise;
}
Finally this is the "submitLogin" procedure that I have in my SingleStepAuthAdapter.js. SingleStepAuthAdapter is the name of the adapter.
//-- exposed methods --//
function submitLogin(auth, username){
WL.Server.setActiveUser("SingleStepAuthAdapter", null);
var input = {
method : 'get',
headers: {Authorization: auth},
path : "/",
returnedContentType : 'plain'
};
var response = "No response";
response = WL.Server.invokeHttp(input);
WL.Logger.info('Response: ' + response.isSuccessful);
WL.Logger.info('response.responseHeader: ' + response.responseHeader);
WL.Logger.info('response.statusCode: ' + response.statusCode);
if (response.isSuccessful === true && (response.statusCode === 200)){
var userIdentity = {
userId: username,
displayName: username,
attributes: {
foo: "bar"
}
};
WL.Server.setActiveUser("SingleStepAuthAdapter", userIdentity);
return {
authRequired: false
};
}
WL.Logger.error('Auth unsuccessful');
return onAuthRequired(null, "Invalid login credentials");
}
So I am trying to send a promise to my controller in order to redirect the user to another page but the promise is not being returned as the challenge handler is not even working.
And by the way, I have followed this tutorial: https://medium.com/#papasimons/worklight-authentication-done-right-with-angularjs-768aa933329c
Does anyone know what this is happening?
Your understanding of the Challenge Handler and mine are considerably different.
Although the
ch.submitAdapterAuthentication()
is similar in structure to the standard adapter invocation methods I have never used any callbacks with it.
I work from the IBM AdapteBasedAuthentication tutorial materials
The basic idea is that your challenge handler should have two callback methods:
isCustomResponse()
handleChallenge()
You will see these functions invoked in response to your submission.
I suggest that start by looking at those methods. I can't comment on the ionic example you reference, but I have myself used angular/ionic with the authentication framework and challenge handlers. My starting point was the IBM material I reference above.
How do you properly delete a subdoc (a task in this case) with AJAX in Mongoose?
Everything seems to be working up until the ajax in the file that's loaded into the page. Or could the problem be in the controller? I have read that you can't perform a .remove on a child element and I'm unclear on how to handle a delete.
Here is the schema:
//new user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
// Task schema
var taskSchema = mongoose.Schema({
clientEasyTask : { type: String },
clientHardTask : { type: String },
clientStupidTask : { type: String }
});
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, lowercase: true },
password: String,
task : [taskSchema]
});
module.exports = mongoose.model('Task', taskSchema);
module.exports = mongoose.model('User', userSchema);
The JS loaded into the page:
// Delete
$(document).ready(function() {
console.log('called del function');
var $alert = $('.alert');
$alert.hide();
$alert.on('error', function(event, data){
$alert.html(data)
$alert.addClass('alert-danger');
$alert.show();
});
$alert.on('success', function(event, data) {
$alert.html(data);
$alert.addClass('alert-info');
$alert.show();
})
$('.task-delete').click(function(event) {
console.log('click event occurred');
$target = $(event.target)
$.ajax({
type: 'DELETE',
url: apiDeleteTask + $target.attr('data-task-id'),
success: function(response) {
$target.parent.children.id(id).remove();
$alert.trigger('success', 'Task was removed.');
},
error: function(error) {
$alert.trigger('error', error);
}
})
});
})
Routes, which matches the working update route:
var tasks = require('./controllers/tasks-controller'),
var User = require('./models/user');
var Task = require('./models/user');
module.exports = function (app, passport) {
// Delete Task
app.delete('/api/tasks/:id', tasks.del);
};
And the tasks-controller.js
var User = require('../models/user');
var Task = require('../models/user');
exports.del = function(req, res, next) {
return User.update({ 'task._id': req.params.id }, { $set: { 'task.$.clientEasyTask': req.body.clientEasyTask }},
(function(err, user) {
if(!user) {
res.statusCode = 404;
return res.send({ error: 'Not phound' });
}
if(!err) {
console.log("Updated Existing Task with ID: " + req.params.id + " to read: " + req.body.clientEasyTask ),
res.redirect('/dashboard');
} else {
res.statusCode = 500;
console.log('Internal error(%d): %s', res.statusCode, err.message);
return res.send({ error: 'Server error' });
}
})
);
};
And last but not least I'm getting this error, that gives the task_id string & line 0:
[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (54c55ac0443873db1eb8c00c, line 0)
In order to remove an entire field from the child array (tasks) the solution is to use $unset. I was wanting to use $set to update the field with a null value, but this is exactly what $unset does.
Here is the line in question that now works:
return User.update({ 'task._id': req.params.id }, { $unset: { 'task.$.clientEasyTask': req.body.clientEasyTask }},
Read more about field operators here: http://docs.mongodb.org/manual/reference/operator/update-field/
$pull would work if you want to remove the array elements without leaving behind a null value, but you must have a specific, matching query. Read about $pull and other array update options here:
http://docs.mongodb.org/manual/reference/operator/update-array/
Also, if you are struggling with a problem I can't stress how important it is to read the documentation. I can guarantee you that everyone on here that is answering problems is doing this, or has learned from someone who does.
Do the work. You'll figure it out. Don't give up.
I am sending a post request via AJAX. The data successfully posts but the AJAX call never completes. Backbone on the front; Node on the back. I am including the save function from my backbone view and the express route.
save: function(event) {
event.preventDefault();
console.log( 'You signed up for ' + this.model.get('name'));
var name = this.model.get('name');
var courseDay = this.model.get('courseDay');
var time = this.model.get('time');
var location = this.model.get('location');
jQuery.post("/test/signups", {
"name" : name,
"courseDay" : courseDay,
"time" : time,
"location" : location,
}, function (data, textStatus, jqXHR) {
console.log("Post response:");
console.dir(data);
console.log(textStatus);
console.dir(jqXHR);
});
}
Route:
app.post('/test/signups', isLoggedIn, function (req, res){
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
user.signup.name = req.body.name;
user.signup.courseDay = req.body.courseDay;
user.signup.time = req.body.time;
user.signup.location = req.body.location;
user.signup.modified = req.body.modified;
user.update({$push: { "signup" :
{ name: user.signup.name,
courseDay: user.signup.courseDay,
time: user.signup.time,
location: user.signup.location,
modified: user.signup.modified
}
}},{safe:true, upsert:true},function(err){
if(err){
console.log(err);
} else {
console.log("Successfully added" + user.signup);
}
});
}
});
});
Your server side code needs to send a response. Try something like below. Note I try to cover all cases of an error, user not found, and user found.
app.post('/test/signups', isLoggedIn, function (req, res){
User.findOne({'_id': req.user.id }, function(err, user) {
if (err) {
return res.status(500).send(err);
}
if (user) {
user.signup.name = req.body.name;
user.signup.courseDay = req.body.courseDay;
user.signup.time = req.body.time;
user.signup.location = req.body.location;
user.signup.modified = req.body.modified;
user.update({$push: { "signup" :
{ name: user.signup.name,
courseDay: user.signup.courseDay,
time: user.signup.time,
location: user.signup.location,
modified: user.signup.modified
}
}},{safe:true, upsert:true},function(err){
if(err){
return res.status(500).send(err);
}
console.log("Successfully added" + user.signup);
res.send(user);
});
} else {
res.status(404).send();
}
});
});