When handling user requests in Action, you often need to get the submitted datas firstly, and then validate them. Only passing the data validation can do the subsquent operations. After the param validation, sometimes, you also need to judge permission. After all of these are correct, it is time to do the real logic process. If these codes are all placed in one Action, it will must make the codes of Action very complex and redundant.
In order to solve this problem, ThinkJS add a layer of Logic
before Controller. The Action in Logic and the Action in Controller are one-to-one correspondence. System will call the Action in Logic automatically before calling the Action in Controller.
The directory of Logic is src/[module]/logic
. When using command thinkjs controller [name]
to create Controller, there will automatically create the corresponding Logic. The codes of the Logic are roughly like the followings.
'use strict';
/**
* logic
* @param {} []
* @return {} []
*/
export default class extends think.logic.base {
/**
* index action logic
* @return {} []
*/
indexAction(){
}
}
The Action in Logic and the Action in Controller are one-to-one correspondence. The Action in Logic also supports __before
and __after
and other magic methods.
Only a couple of request types will be used in one specific Action. You can config some specific request types to validate them.
export default class extends think.logic.base {
indexAction(){
this.allowMethods = 'post'; //allow post request only
}
testAction(){
this.allowMethods = 'get,post'; //allow get and post requests only
}
}
The config of data validation is as follows.
export default class extends think.logic.base {
indexAction(){
let rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
}
}
The config format is field name
-> config
, each field config supports multiple validation types. The multiple validation types are separated by |
, the validation type and param are separated by :
, param and param are seperated by ,
.
Params could follow the end of validation type. Besides supporting simply string params separated by commas, it also supports the complex param in JSON format.
export default class extends think.logic.base {
indexAction(){
let rules = {
field1: "array|default:[1,2]", // param is an array
field2: 'object|default:{\"name\":\"thinkjs\"}' //param is an object
}
}
}
ThinkJS also allow you config it with object like:
export default class extends think.logic.base {
indexAction(){
let rules = {
field1: {required: true, array: true, default: [1, 2]}, //parameter is array
field2: {object: true, default: {name: "thinkjs"}} //parameter is object
}
}
}
The supported data types include boolean
,string
,int
,float
,array
,object
. And the default type is string
.
Use default:value
to define the default value of field. If the value of current field is empty, it will be overrided by the default one. What you get subsequently will be the default value.
By default, get the field value according to the current request type. If the type of current request is GET, use this.get('version')
to get the value of version
field. If the type of current request is POST, use this.post
to get the field value.
But sometimes in the POST type, you may want to get the params from uploaded file or URL. By this time, you need to specify the way to get data. The supported ways to get data are get
,post
and file
.
export default class extends think.logic.base {
/**
* save data, POST request
* @return {} []
*/
saveAction(){
let rules = {
name: "required",
image: "object|file|required",
version: "string|get|in:1.2,2.0|default:2.0"
}
}
}
The above demo specifys to use post
method to get the value of the field name
, use file
method to get the value of the field image
, use get
method to get the value of the field version
.
The above config only specify the certain validation rules but not the error message when validation failure. Error messages support internationalizaion, you need to define it in the config file src/common/config/locale/[lang].js
. eg.
// src/common/config/locale/en.js
export default {
validate_required: '{name} can not be blank',
validate_contains: '{name} need contains {args}',
}
The key is validate_
+ validation type name
. The value supports two params: {name}
and {args}
, which respectively indicate the field name and the passed param.
If you want to define the detailed message of a certain error type for a specific field, you could add a field name to the end. eg.
// src/common/config/locale/en.js
export default {
validate_required: '{name} can not be blank',
validate_required_email: 'email can not be blank', //specify the error message of required for email field
}
After configing the validation rules, you can use the method this.validate
to validate. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
let flag = this.validate(rules);
if(!flag){
return this.fail('validate error', this.errors());
}
}
}
If the return value is false
, you could use method this.errors
to get the detailed error message. After getting the error message, you could use method this.fail
to output it in JSON format, or use method this.display
to output a page.
In template, you can get the error message by errors
field. The following is the way to show error message (taking ejs template as an example).
<%for(var field in errors){%>
<%-field%>:<%errors[field]%>
<%}%>
In generally, there will output a JSON message after validation failure. If this.validate
needs to be called manually evertime to validate in Action of Logic, it must be inconvenient. You can make the validation automatically by assigning validation rules to this.rules
. eg.
export default class extends think.logic.base {
indexAction(){
this.rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
}
}
After assigning validation rules to this.rules
, the validation will be automatically done after Action execution. If there are errors, it will directly output error messages in JSON format. Automatical validation uses the magic method __after
to complete.
Required Item.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'required' //the value of name is required
}
}
}
When the value of the other certain item is one of the specified values, this item is required. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'requiredIf:email,admin@example.com,admin1@example.com'
}
}
}
When the value of email
is one of admin@example.com
and admin1@example.com
, the value of name
is required.
When the value of the other certain item is not one of the specified values, this item is required. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'requiredNotIf:email,admin@example.com,admin1@example.com'
}
}
}
When the value of email
is not one of admin@example.com
or admin1@example.com
, the value of name
is required.
When one of the values of some other certain items does exist, this item is required. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'requiredWith:email,title'
}
}
}
When one of the values of email
and title
does exist, the value of name
is required.
When all of the values of some other certain items do exist, this item is required. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'requiredWithAll:email,title'
}
}
}
When all of the values of email
and title
do exist, the value of name
is required.
When one of the values of some other certain items does not exist, this item is required. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'requiredWithout:email,title'
}
}
}
When one of the values of email
and title
does not exist, the value of name
is required.
When all of the values of some other certain items do not exist, this item is required. eg.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'requiredWithoutAll:email,title'
}
}
}
When all of the values of email
and title
do not exist, the value of name
is required.
The value needs to contain the certain value.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'contains:thinkjs' //need to contain string 'thinkjs'。
}
}
}
Be equal to the value of the other item.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'equals:firstname'
}
}
}
The value of name
needs to be equal to the value of firstname
.
Be different to the value of the other item.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'different:firstname'
}
}
}
The value of name
can't to be equal to the value of firstname
.
The value needs to be before a certain date. By default, it needs to be before the current date.
export default class extends think.logic.base {
indexAction(){
let rules = {
start_time: 'before', //need to be before the current date
start_time1: 'before:2015/10/12 10:10:10' //need to be before 2015/10/12 10:10:10
}
}
}
The value needs to be after a certain date. By default, it needs to be after the current date.
export default class extends think.logic.base {
indexAction(){
let rules = {
end_time: 'after', //need to be after the current date
end_time1: 'after:2015/10/10' //need to be after 2015/10/10
}
}
}
The value must only consist of [a-zA-Z].
export default class extends think.logic.base {
indexAction(){
let rules = {
en_name: 'alpha'
}
}
}
The value of en_name
must only consist of [a-zA-Z].
The value must only consist of [a-zA-Z_].
The value must only consist of [a-zA-Z0-9].
The value must only consist of [a-zA-Z0-9_].
The value must only consist of ascii.
The value must only consist of base64.
The length of bytes needs to be in a certain range.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'byteLength:10' // the length of bytes can not less than 10
name1: 'byteLength:10,100' //the length of bytes must be in the range of 10 to 100
}
}
}
The value needs to be a credit card number.
The value needs to be a currency.
The value needs to be a date.
The value needs to be a decimal.
The value needs to be divisible by a number.
export default class extends think.logic.base {
indexAction(){
let rules = {
count: 'divisibleBy:3' //could to be divisible by 3
}
}
}
The value needs to be email format.
The value needs to be a qualified domain name.
The value needs to be a float.
export default class extends think.logic.base {
indexAction(){
let rules = {
money: 'float' //need to be a float
money1: 'float:3.2' //need to be a float, and the minimum is 3.2
money2: 'float:3.2,10.5' //need to be a float, and the minimum is 3.2, the maximum is 10.5
}
}
}
The value needs contain full width char.
The value needs contain half width char.
The value needs to be a hex color value.
The value needs to be hex.
The value needs to be ip format.
The value needs to be ip4 format.
The value needs to be ip6 format.
The value needs to be a book serial number.
The value needs to be ISIN (International Securities Identification Numbers).
The value needs to be iso8601 date format.
The value needs to be in some certain values.
export default class extends think.logic.base {
indexAction(){
let rules = {
version: 'in:1.2,2.0' //need to be one of 1.2,2.0
}
}
}
The value needs to be not in some certain values.
export default class extends think.logic.base {
indexAction(){
let rules = {
version: 'noin:1.2,2.0' //need to be not in 1.2,2.0
}
}
}
The value needs to be int.
export default class extends think.logic.base {
indexAction(){
let rules = {
value: 'int' //int
value1: 'int:1' //can not less than 1
value2: 'int:10,100' //need to be in the range of 10 to 100
}
}
}
The value can not less than the certain value.
export default class extends think.logic.base {
indexAction(){
let rules = {
value: 'min:10' //can not less than 10
}
}
}
The value can not great than the certain value.
export default class extends think.logic.base {
indexAction(){
let rules = {
value: 'max:10' //can not great than 10
}
}
}
The length needs to be in the certain range.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'length:10' //the length can not less than 10
name1: 'length:10,100' //the length need to be in the range of 10 to 100
}
}
}
The length can not to be less than the min-length.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'minLength:10' //the length can not to be less than 10
}
}
}
The length can not to be great than the max-length.
export default class extends think.logic.base {
indexAction(){
let rules = {
name: 'maxLength:10' //the length can not to be great than 10
}
}
}
The value needs to be all lowercase.
The value needs to be all uppercase.
The value needs to be a mobile phone.
export default class extends think.logic.base {
indexAction(){
let rules = {
mobile: 'mobile:zh-cn' //must be a chinese mobile phone
}
}
}
The value is the ObjectID of MongoDB.
Include multibyte char.
The value is url.
Database query order, like name DESC.
Database query field, like name,title.
Whether the file uploaded is a pic
The value starts with some certain chars.
The value ends with some certain chars.
The value is string.
The value is array.
The value is boolean.
The value is object.
RegExp, such as:
export default class extends think.logic.base {
indexAction(){
this.rules = {
number: {
required: true,
regexp: /^\d{6}$/
}
}
}
}
If the default supported validation types can not meet the demand, you can use the method think.validate
to extend the validation types. eg.
// src/common/bootstrap/validate.js
think.validate('validate_name', (value, ...args) => {
//need to return true or false
//true-validate sucess, false-validate fail
})
The above registers a validation type named validate_name
, thus, you can directly use this validation type in Logic.
If you want to parse args
, you can register a function. eg. the name of the above validation type is validate_name
, then the corresponding name of parse param is _validate_name
, that is _
+ validation type
.
think.validate('_validate_name', (args, data) => {
let arg0 = args[0];
args[0] = data[arg0].value; //parse the first param field name to the corresponding param value
return args;
})