I have a custom validation method that checks for duplicate usernames. The json returns correctly for notDuplicateUsername but the validation always shows up as invalid.
$('#register-form').validate({
//see source of http://jquery.bassistance.de/validate/demo/ for example
rules: {
schoolname: {
required: true
},
username: {
required: true,
notDuplicateUsername: true
},
password: {
required: true
},
email: {
required: true,
email: true
}
},
messages: {
schoolname: 'Please tell us where you want to use Word Strip.',
username: {
required: 'Please choose a username.',
notDuplicateUsername: 'Sorry, that username is already being used.'
},
password: 'Please choose a password.',
email: 'Please can we have your email address.'
}
});
jQuery.validator.addMethod(
'notDuplicateUsername',
function(value, element, params){
var toCheck = new Object();
toCheck['username'] = $('#username').val();
var data_string = $.toJSON(toCheck);//this is a method of the jquery.json plug in
$.post('check_duplicate_username.php', {username_data: data_string}, function(result){
var noDuplicate = true;
var returned_data = $.evalJSON(result);//this is a method of the jquery.json plug in
if (returned_data.status == 'duplicate'){
noDuplicate = false;
}
console.log('value of noDuplicate: '+noDuplicate);
return noDuplicate;
});
}
);
Any clues anyone?
Probably you might have sorted this out already, but just in case. I faced a similar problem and found that I was comparing wrong types. May be your server sends the status as a boolean or some other datatype and its comparison to a string fails and always returns false.
Related
I have divided the data entry in a REST call in 4 parts. Data can be sent to REST call via:-
headers
query params
path params
request body
So in order to validate the presence of any key in any of the above 4 parts I have created a schema in this format. So if in case I have to validate anything in query params I will add the key 'query' and then add the fields inside that, that needs to be validated
const schema = {
id: 'Users_login_post',
type: 'object',
additionalProperties: false,
properties: {
headers: {
type: 'object',
additionalProperties: false,
properties: {
Authorization: {
type: 'string',
minLength: 10,
description: 'Bearer token of the user.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length',
required: 'should have Authorization'
}
}
},
required: ['Authorization']
},
path: {
type: 'object',
additionalProperties: false,
properties: {
orgId: {
type: 'string',
minLength: 23,
maxLength: 36,
description: 'OrgId Id of the Organization.',
errorMessages: {
type: 'should be a string',
minLength: 'should be atleast of 23 length', // ---> B
maxLength: 'should not be more than 36 length',
required: 'should have OrgId'
}
}
},
required: ['orgId']
}
}
};
Now, in my express code, I created a request object so that I can test the validity of the JSON in this format.
router.get("/org/:orgId/abc", function(req, res){
var request = { //---> A
path: {
orgId : req.params.orgId
},
headers: {
Authorization : req.headers.Authorization
}
}
const Ajv = require('ajv');
const ajv = new Ajv({
allErrors: true,
});
let result = ajv.validate(schema, request);
console.log(ajv.errorsText());
});
And I validate the above request object (at A) against my schema using AjV.
The output what I get looks something like this:
data/headers should have required property 'Authorization', data/params/orgId should NOT be shorter than 23 characters
Now I have a list of concerns:
why the message is showing data word in the data/headers and data/params/orgId even when my variable name is request(at A)
Also why not my errormessages are used, like in case of orgId I mentioned: should be atleast of 23 length (at B) as a message, even then the message came should NOT be shorter than 23 characters.
How can I show request/headers instead of data/headers.
Also, the way I used to validate my path params, query params, header params, body param, is this the correct way, if it is not, then what can be the better way of doing the same?
Please shed some light.
Thanks in advance.
Use ajv-keywords
import Ajv from 'ajv';
import AjvKeywords from 'ajv-keywords';
// ajv-errors needed for errorMessage
import AjvErrors from 'ajv-errors';
const ajv = new Ajv.default({ allErrors: true });
AjvKeywords(ajv, "regexp");
AjvErrors(ajv);
// modification of regex by requiring Z https://www.regextester.com/97766
const ISO8601UTCRegex = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?Z$/;
const typeISO8601UTC = {
"type": "string",
"regexp": ISO8601UTCRegex.toString(),
"errorMessage": "must be string of format 1970-01-01T00:00:00Z. Got ${0}",
};
const schema = {
type: "object",
properties: {
foo: { type: "number", minimum: 0 },
timestamp: typeISO8601UTC,
},
required: ["foo", "timestamp"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { foo: 1, timestamp: "2020-01-11T20:28:00" }
if (validate(data)) {
console.log(JSON.stringify(data, null, 2));
} else {
console.log(JSON.stringify(validate.errors, null, 2));
}
https://github.com/rofrol/ajv-regexp-errormessage-example
AJV cannot know the name of the variable you passed to the validate function.
However you should be able to work out from the errors array which paths have failed (and why) and construct your messages from there.
See https://ajv.js.org/#validation-errors
To use custom error messages in your schema, you need an AJV plugin: ajv-errors.
See https://github.com/epoberezkin/ajv-errors
I have a sails app working with just a simple index and simple create (insert) to a mongo db. When I enter correctly typed data hard coded to be the type stated in the model, I get an error.
url insert err = [Error (E_VALIDATION) 1 attribute is invalid] Invalid attributes sent to urls:
• status
• Value should be a number (instead of "0", which is a string)
This is a very small, new project so not a lot of settings have been changed from default.
Since I have console.log in the create, I can see exactly what I' sending to the urls.create:
{ url: 'http://www.dina.com',
status: 0,
statusDate: '2016-11-19T19:46:10.804Z' }
I'm not doing anything to enforce type and it looks like I'm obeying type. Why am I getting error?
The model looks like:
// urls.js
module.exports = {
attributes: {
url : { type: 'string' },
status: { type: 'number'},
statusDate: {type: 'date'}
}
};
My config/models.js has schema set to false:
// config/models.js
module.exports.models = {
connection: 'DigitalOceanMongodbServer',
migrate: 'safe',
schema: false
};
My controller creates a new object with hard-coded status and statusDate of the correct type:
// urlsController.js
create: function (req, res) {
let url = req.body.url;
if(!url) return res.json({failure: 'empty url'});
let isValid = sails.validurl.isUri(url);
if(!isValid) return res.json({failure: 'url is not valid'});
let newObj = {
url: url,
status: 0, <---- obviously a number
statusDate: new Date().toISOString() <---obviously a date
}
console.log(newObj);
urls.create(newObj).exec(function createCB(err,created){
if (err){
return res.negotiate(err);
} else {
return res.ok(created);
}
});
}
Specify "integer" instead of "number" in the type of your model. I did not find "number" in the docs.
See: http://sailsjs.org/documentation/concepts/models-and-orm/attributes
I have a knockout pureComputed observable that is supposed to return if a account number is valid.
First it checks if the field is empty. (returns false)
Then it checks to see if the account number has changed from what was loaded. (returns true)
Finally it will run the ajax call to get the account info.
If successful it will set the ErrorMessage from the object and the Customer info. Then it is supposed to return true or false based on the ErrorMessage.
If unsuccessful it will set the ErrorMessage and return false.
self.isAccountValid = ko.computed(function () {
if (!self.account()) {//If not complete mark as InValid
self.accountError("Account Number Incomplete.")
return false;
} else if (vm.account == self.account()) {
self.accountError("");
return true;
} else {
var deferred = $.Deferred();
return $.ajax({
url: '/Order/NumberValidation',
data: { account: self.account() }
}).success(function (data) {
self.accountError(data.ErrorMessage);
//Set customer info properties
self.setCustomerInfo(data);
//Set success or warn icon based on error message field
var isValid = !data.ErrorMessage;
deferred.resolve(isValid);
return deferred;
}).error(function (data) {
//Set error message for invalid account
self.accountError("Server error. Please try again in a few minutes.");
//Set warning icon
return false;
})
}
}).extend({ async: ko.observable() });
They extend is a method I found to allow the computed to wait on the ajax call:
ko.extenders.async = function (nonObservableComputed, observableResult) {
nonObservableComputed.subscribe(function (result) {
jQuery.when(result).then(observableResult);
});
return observableResult;
}
This is setting the other data correctly on success, but the return of !data.ErrorMessage which evaluates to false if looking at it through debug is not what is set for the value of isAccountValid. Instead it is being set to the whole data object even though I am trying to return just the boolean.
myViewModel.isAccountValid()
Object {FirstName: null, LastName: null, StreetAddress: null, City: null, State: null…}
City: null
ErrorMessage: "Sequence contains no matching element"
FirstName: null
LastName: null
PhoneNumber: 0
State: null
StreetAddress: null
ZipCode: 0
One problem is that you're using a pureComputed for a function with side effects.
You also have an extra c in acccountError in your success section.
The extender is pretty horrible. It becomes trivial if you pass it the computed you want to use instead of its type, and don't need the added inProgress member.
ko.extenders.async = function (nonObservableComputed, observableResult) {
nonObservableComputed.subscribe(function (result) {
jQuery.when(result).then(observableResult);
});
return observableResult;
}
Demo: http://jsfiddle.net/sp160tan/1/
Update
Pretty sure your problem is that you're returning the ajax result rather than the deferred. Also, the success and error callbacks don't need to return anything; their return values are not used. This could be a source of confusion.
} else {
var deferred = $.Deferred();
$.ajax({
url: '/Order/NumberValidation',
data: { account: self.account() }
}).success(function (data) {
self.accountError(data.ErrorMessage);
//Set customer info properties
self.setCustomerInfo(data);
//Set success or warn icon based on error message field
var isValid = !data.ErrorMessage;
deferred.resolve(isValid);
}).error(function (data) {
//Set error message for invalid account
self.accountError("Server error. Please try again in a few minutes.");
//Set warning icon
deferred.resolve(false);
})
return deferred;
}
I have a three field form made of a name field, email field and a textarea. I'm using Joi 4.7.0 version along with hapijs. I use the object below validate the input. I receive the data object from an ajax call. When I fill all the three fields with wrong informations I get only the message relative to the first wrong field. Like that:
"{"statusCode":400,"error":"Bad Request","message":"name is not allowed to be empty","validation": {"source":"payload","keys":["data.name"]}}"
validate: {
payload: {
data: {
name: Joi.string().min(3).max(20).required(),
email: Joi.string().email().required(),
message: Joi.string().min(3).max(1000).required()
}
}
}
For explanation let suppose to not fill the three field. I get only one message error and not the message error of the others fields. Why?
It happens because Joi aborts early by default.
abortEarly - when true, stops validation on the first error, otherwise returns all the errors found. Defaults to true.
*EDIT: Configuration has changed in hapi 8.0. You need to add abortEarly: false to the routes config:
var server = new Hapi.Server();
server.connection({
host: 'localhost',
port: 8000,
routes: {
validate: {
options: {
abortEarly: false
}
}
}
});
*See the Joi API documentation for more details.
*Also, see validation under Hapi Route options.
So Joi stops the validation on the first error:
var Hapi = require('hapi');
var Joi = require('joi');
var server = new Hapi.Server('localhost', 8000);
server.route({
method: 'GET',
path: '/{first}/{second}',
config: {
validate: {
params: {
first: Joi.string().max(5),
second: Joi.string().max(5)
}
}
},
handler: function (request, reply) {
reply('example');
}
});
server.start();
server.inject('/invalid/invalid', function (res) {
console.log(res.result);
});
Outputs:
{ statusCode: 400,
error: 'Bad Request',
message: 'first length must be less than or equal to 5 characters long',
validation: { source: 'params', keys: [ 'first' ] } }
You can however configure Hapi to return all errors. For this, you need to set abortEarly to false. You can do this in server configuration:
var server = new Hapi.Server('localhost', 8000, { validation: { abortEarly: false } });
If you run the script now, you get:
{ statusCode: 400,
error: 'Bad Request',
message: 'first length must be less than or equal to 5 characters long. second length must be less than or equal to 5 characters long',
validation: { source: 'params', keys: [ 'first', 'second' ] } }
I'm not integrating with hapi.js, but I noticed that there is a ValidationOptions object which can be passed along. Inside that object is an abortEarly option, so this should work:
Joi.validate(request, schema, { abortEarly: false }
This can also be configured as follows:
Joi.object().options({ abortEarly: false }).keys({...});
Check out these type definitions for more ValidationOptions:
https://github.com/DefinitelyTyped/tsd/blob/master/typings/joi/joi.d.ts
The validation key no longer works with the Hapi.Server constructor in Hapi 8.0:
[1] validation is not allowed
I found the solution in a GitHub issue for hapi:
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({
host: HOST,
port: PORT,
routes: {
validate: {
options: {
abortEarly: false
}
}
}
});
// Route using Joi goes here.
server.route({});
server.start(function () {
console.log('Listening on %s', server.info.uri);
});
After some research, I found out it can be solved 2 ways:
[Segments.BODY]: Joi.object().keys({
value: Joi.string().required().error(new Error('Value is required and has to be a text!')),
})
or
[Segments.BODY]: Joi.object().keys({
password: Joi.string().required().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).min(8).label('Password').messages({
'string.pattern.base': 'Your {#label} does not matche the suggested pattern',
'string.base': `Your {#label} should match the suggested pattern`,
'string.empty': `Your {#label} can not be empty`,
'string.min': `Your {#label} has to be at least {#limit} chars`,
'any.required': `Your {#label} is required`,
}),
})
I am using ember-validations to validate a model in a form.
If I create the record with createRecord the instance of the model is already validated and therefore the form already shows validations errors before the user inputs values.
I just want to validate the model before submitting the form. Is there a way?
You need to add a conditional validator ('if' or 'unless') and activate it only when submitting the form.
Here is a quick example: http://jsbin.com/letujimu/1/edit?html,js,output
There is another alternative to ember-validations. ember-model-validator, with this addon you decide when to validate. By including Ember-model-validator's mixin into your model, this will add a validate function to your model, it is a synchronous function which returns either true or false.
There is support for all these validations:
Presence
Acceptance
Absence
Format
Length
Email
Color
ZipCode
Subdomain
URL
Inclusion
Exclusion
Numericality
Match
Password
CustomValidation
Relations
Example:
// Your model
import Validator from '../mixins/model-validator';
export default DS.Model.extend(Validator, {
email: DS.attr('string'),
password: DS.attr('string'),
passwordConfirmation: DS.attr('string'),
validations: {
email: {
presence: true,
email: { message: 'is not a valid email' }
},
password: {
presence: true,
length: {
minimum: 6
}
},
passwordConfirmation: {
presence: true,
match: 'password'
}
}
});
import Ember from 'ember';
export default Ember.Route.extend(
{
actions: {
saveFakeModel: function() {
var _this = this,
fakeModel = this.get('model');
if(fakeModel.validate()){
fakeModel.save().then(
// Success
function() {
// Alert success
console.log('ooooh yeah we just saved the FakeModel...');
},
// Error handling
function(error) {
// Alert failure
console.log('There was a problem saving the FakeModel...');
console.log(error);
}
);
}else{
fakeModel.get('errors');
}
},
}
}
);