概述
LoopBack [模型生成器](Model-generator.html)为server/models
或common / models
目录中的每个模型创建一个模型JSON文件(取决于模型是仅服务器还是在 服务器和客户端)。 该文件名为model-name.json
,其中_model-name
_是模型名称; 例如,customer.json
。模型JSON文件定义模型,模型之间的关系以及对模型的访问。
LoopBack模型生成器自动将驼峰模型名称(例如MyModel)转换为小写虚线名称(my-model)。 例如,如果使用模型生成器创建名为“FooBar”的模型,则会在common/models中创建文件foo-bar.json和foo-bar.js。 但是,模型名称(“FooBar”)将通过模型的name属性保留。
例如,以下是客户模型的模型定义文件的部分内容
customer.json
{
"name": "Customer", // See Top-level properties below
"description": "A Customer model representing our customers.",
"base": "User",
"idInjection": false,
"strict": true,
"options": { ... }, // See Options below - can also declare as top-level properties
"properties": { ... }, // See Properties below
"hidden": [...], // See Hidden properties below
"validations": [...], // See Validations below
"relations": {...}, // See Relations below
"acls": [...], // See ACLs below
"scopes": {...}, // See Scopes below
"indexes" : { ...}, // See Indexes below
"methods": [...], // See Methods below
"remoting": {
"normalizeHttpPath": true
},
"http": {"path": "/foo/mypath"}
}
顶层属性
- acls: 数组类型;默认为空;一组描述模型访问控制的ACL规范。
- base: 字符串类型;默认无;此模型扩展的另一个模型的名称。 该模型将“继承”基础模型的属性和方法。
- description: 字符串或数组;默认无;模型的可选描述,您可以将长描述拆分为字符串(行)数组,以保持行长度可管理, 例如:
["Lorem ipsum dolor sit amet, consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore",
"magna aliqua."]
-
excludeBaseProperties: 数组类型;默认为
['id', 'password']
;从基本模型中排除给定的属性列表,使其不可见。 使用此代替下面“从基本模型中排除属性”部分中记录的方法。 -
forceId: boolean型;默认为
true
;如果为true,则阻止客户端手动设置自动生成的ID值。 -
http.path: string型;默认为空;此模型的REST端点的自定义HTTP路径。
-
strict: boolean或string型;默认false,但如果数据源由关系数据库支持,则default为true。指定模型是否仅接受预定义属性之一:
- true:仅接受模型中定义的属性。 如果要确保模型仅接受预定义属性,请使用此选项。 如果您尝试使用未预定义的属性保存模型实例,LoopBack将抛出ValidationError。
- false:模型是一个开放模型,接受所有属性,包括模型中未预定义的属性。 此模式对于将自由格式JSON数据存储到无架构数据库(如MongoDB)非常有用。
- “filter”:仅接受模型中定义的属性。 如果使用未预定义的属性加载或保存模型实例,LoopBack将忽略它们。 在处理您希望在没有迁移脚本的情况下丢失的旧数据时,这尤其有用。
-
idInjection: Boolean型;默认为
true
; 是否自动向模型添加id属性:- true:id属性自动添加到模型中。 这是默认值。
- false:不自动将id属性添加到模型中。
-
name: string;默认为空;model得名字
-
options: Object对象;默认为无;指定模型选项的JSON对象。
-
plural: string;默认使用标准英语惯例的name复数。
-
properties: Object对象;默认无;JSON对象,用于指定模型中的属性。
-
relations: Object对象;默认无;包含关系名称和关系定义的对象。
-
remoting.normalizeHttpPath: Boolean型;默认
false
;如果为true
,则在HTTP路径中转换:- 大写字母为小写。
- 下划线(_)为短划线( - )。
- CamelCase以划线划分。
不影响占位符(例如“:id”)。 例如,“MyClass”或“My_class”变为“my-class”。
-
replaceOnPUT: Boolean型;默认
false
;如果为true,则replaceOrCreate()
和replaceById()
使用HTTP PUT方法; 如果为false,则updateOrCreate()
和updateAttributes()/patchAttributes()
使用HTTP PUT方法。 -
scopes: Object对象;默认为无;参考下面章节内容。
Options
options
键指定高级选项,例如特定于数据源的选项。
Options中的设置优先与顶级设置
高级选项
- validateUpsert: Boolean型;
默认情况下,upsert()(updateOrCreate())方法不会强制执行有效的模型数据。 相反,它会将验证错误记录到控制台。 这保留了与旧版2.x版本的向后兼容性。
将此属性设置为true可确保在验证失败时upsert()返回错误。 LoopBack的下一个主要版本将默认启用此选项(设置为true)。
将此属性设置为false可防止upsert()完全调用任何验证器。
默认情况下,upsert()调用所有验证程序并将任何验证错误报告给控制台日志。
- allowEternalTokens: Boolean型;允许使用ttl = -1创建的访问令牌永不过期。
特定于数据源的选项
当模型附加某种类型的数据源(如Oracle或MySQL)时,您可以将数据库模式和表的名称指定为具有连接器类型名称的键下的属性。此键的值必须与值匹配 在[datasources.json](datasources.json.html)中相应的connector
属性。例如,在下面的代码片段中,datasources.json
中会有一个条目,如:“myDB”:{“ name“:”myDB“,”connector“:”mysql“,...}
。
...
"options": {
"mysql": {
"table": "location"
},
"mongodb": {
"collection": "location"
},
"oracle": {
"schema": "BLACKPOOL",
"table": "LOCATION"
}
},
...
属性
属性键定义一个或多个属性,每个属性都是具有下表中描述的键的对象。 下面是一个基本属性定义的示例:
...
"properties": {
"firstName": {
"type": "String",
"required": "true"
},
"id": {
"type": "Number",
"id": true,
"description": "User ID"
},
...
model属性对象的常用属性
每个model属性都可以具有下表中描述的属性。 只需要type
属性; 对于只有type
的属性,您可以使用以下简写:
"propertyName": "type"
或例如:
...
"emailVerified": "boolean",
"status": "string",
...
- default: 非必须;any类型;属性的默认值。 类型必须与指定的类型匹配。
- defaultFn: 非必填;string类型;要调用以设置属性的默认值的函数的名称。 必须是以下之一:
- “guid”:使用计算机MAC地址和当前时间(UUID版本1)生成新的全局唯一标识符(GUID)。
- “uuid”:使用计算机MAC地址和当前时间(UUID版本1)生成新的通用唯一标识符(UUID)。
- “uuidv4”:使用UUID版本4算法生成新的通用唯一标识符。
- “now”:使用新Date()返回的当前日期和时间。
- description: 非必须;String or Array;该物业的文件。 您可以将长描述拆分为字符串(行)数组,以保持行长度的可管理性。 例如:
[
"Lorem ipsum dolor sit amet, consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore",
"magna aliqua."
]
- doc: 非必须;string;属性文档,不推荐使用,请改用“description”。
- id: 非必填;Boolean;该属性是否是唯一标识符。 默认值为false。
- index: 非必填;Boolean;属性是否表示作为数据库索引的列(字段)。
- required: 非必填;Boolean;是否需要属性值。 如果为true,则添加或更新模型实例需要该属性的值。
- type: 必填;String;属性值的类型。
- *: 非必填;Any对象;参考后面章节内容。
ID属性
表示要在数据库中持久保存的数据的模型通常具有一个或多个_ID属性_,它们唯一地标识模型实例。例如,user
模型可能具有用户ID。
默认情况下,如果没有定义ID属性并且idInjection
属性为true
(或未设置,因为true
是默认值),LoopBack会自动向模型添加id
属性,如下所示:
id: {type: Number, generated: true, id: true}
generated
属性表示数据库将自动生成ID。如果是,则连接器决定用于自动生成密钥的类型。 对于关系数据库,例如Oracle或MySQL,它默认为“number”。如果您的应用程序生成唯一ID,请将其设置为false。
要将属性显式指定为ID,请将选项的id
属性设置为true
。 id
属性值必须是以下之一:
true
:属性是一个ID。false
(或任何转换为false的值):该属性不是ID(默认值)。- 正数,例如1或2:属性是复合ID的索引。
在数据库术语中,ID属性是主键列。 这些属性定义为’id’属性设置为true或数字作为复合键的位置。
{
"myId": {
"type": "string",
"id": true
}
}
- 如果模型没有显式定义的ID属性,LoopBack会自动注入名为“id”的属性,除非
idInjection
选项设置为false。 - 如果ID属性的“generated”设置为true,则连接器决定用于自动生成的密钥的类型。 例如,对于SQL Server,它默认为“number”。
- 如果模型由数据库支持,LoopBack CRUD方法期望模型具有“id”属性。
- 没有任何“id”属性的模型只能在不附加到数据库的情况下使用。
复合ID
LoopBack支持定义具有多个属性的复合ID。 例如:
var InventoryDefinition = {
productId: {type: String, id: 1},
locationId: {type: String, id: 2},
qty: Number
}
这里 (productId, locationId) 将作为Inventory Modle的复合ID
当前,REST API和DAO方法(如findOrCreate,updateOrcreate和replaceOrCreate)中不支持复合ID作为查询参数。
数据映射属性
使用关系数据库数据源时,可以指定以下描述数据库中列的属性。
- columnName: string;字段列名。
- dataType: string;由数据库定义的字段类型字面量。
- dataLength: Number;数据长度。
- dataPrecision: Number;数字数据精度
- dataScale: Number;数字数据量表
- nullable: Boolean;数据允许为空
例如,要将属性映射到Oracle数据库表中的列,请使用以下命令:
...
"name": {
"type": "String",
"required": false,
"length": 40,
"oracle": {
"columnName": "NAME",
"dataType": "VARCHAR2",
"dataLength": 40,
"nullable": "Y"
}
}
...
从基本模型中排除属性
默认情况下,模型从基础继承所有属性。 要排除某些基本属性不可见,需要设置excludeBaseProperties=['property-be-excluded-from-base-model']
。
例如:
common/models/customer.json
...
"base": "User",
"excludeBaseProperties" = ["lastUpdated", "credentials", "challenges"],
"properties": {
...
}
...
或
...
"base": "Model",
"excludeBaseProperties" = ["id"],
"properties": {
...
}
...
建议不要通过将base属性设置为’null’或’false’来排除基本属性。 相反,使用如上所示的excludeBaseProperties
。
common/models/customer.json
...
"base": "User",
"properties": {
"lastUpdated": false,
"credentials": null,
"challenges": null,
"modified": "date"
}
...
隐藏属性
隐藏属性不会在应用程序的HTTP响应中的JSON数据中发送。属性值是一个字符串数组,并且数组中的每个字符串必须与为模型定义的属性名称匹配。
common/models/user.json
{
...
"properties": {
...
"password": {
"type": "string",
"required": true
},
...
},
"hidden": ["password", "verificationToken"],
...
}
如果要将返回的字段列入白名单而不是将其列为黑名单,请考虑:
- 在模型默认
scope
上操作的fields
。 这将在数据库响应层运行,因此限制了您检查数据库中您不希望暴露给外部世界的字段的能力(例如,私有标志)。 - 覆盖模型的
toJSON
方法
请参阅GitHub。
受保护的属性
protected
属性是一个字符串数组,数组中的每个字符串必须与为模型定义的属性名称匹配。
如果对象嵌套在另一个对象中,则不会在HTTP响应JSON数据中发送受保护的属性。例如,假设有一个Author对象和一个Book对象。 Book与Author有关系,Book是一个公共API。作者模型有个人信息(如社会安全号码)应该被“保护”,所以任何查找书籍作者的人都不会得到这些信息。
隐藏和受保护的属性略有不同。
- hidden: 确定当前查询所对应的模型的返回属性。
- protected:确定当前查询所对应的关联模型返回属性。
将电子邮件配置为受保护属性的示例:
common/models/user.json
{
...
"properties": {
...
"email": {
"type": "string",
"required": true
},
...
},
"protected": ["email"],
...
}
验证
这尚未实施。 您当前必须在代码中实现验证逻辑;
Key | Type | Description |
---|---|---|
default | Any | Default value of the property. |
required | Boolean | Whether the property is required. |
pattern | String | Regular expression pattern that a string should match |
max | Number | Maximum length for string types. |
min | Number | Minimum length for string types. |
length | Number | Maximum size of a specific type, for example for CHAR types. |
"username": {
"type": "string",
"description": "User account name",
"min": 6,
"max": 24
}
关联关系
relations
键通过JSON对象定义模型之间的关系。此对象中的每个键是相关模型的名称,值是JSON对象,如下表所述。
例如:
...
"relations": {
"accessTokens": {
"model": "accessToken",
"type": "hasMany",
"foreignKey": "userId"
},
"account": {
"model": "account",
"type": "belongsTo"
},
"transactions": {
"model": "transaction",
"type": "hasMany"
}
},
...
- foreignKey: string;用于查找相关模型实例的可选外键。
- keyThrough: string;要在HasMany关系中使用的外键。
- model: string;相关模型的名称,必填。
- type: string; 关系类型,必填。
- through: string;创建hasManyThrough关系的模型的名称。
- options.disableInclude: boolean: 如果在include语句中使用关系,则不获取数据
- options.http.path: string;设置关系http路径
hasManyThrough的例子:
"patient": {
"model": "physician",
"type": "hasMany",
"through" : "appointment"
}
ACLs
acls
键的值是一个描述模型访问控制的对象数组。 每个对象都有下表中描述的键。
"acls": [
{
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "$everyone",
"property": "myMethod"
},
...
]
-
accessType: string; 访问的类型,包括:
- READ
- WRITE
- EXECUTE
-
- (default)
-
permission: string; 授予的权限类型。必填
- ALLOW: 明确授予对资源的访问权限。
- DENY: 明确拒绝访问资源。
-
principalId: string; 访问主体标识,必填
- 用户id
- 应用id
- 以下预定义动态角色之一:
- $everyone: 所有人
- $owner: 访问资源所有者
- $authenticated: 认证用户
- $unauthenticated: 未认证用户
- 自定义角色名称
- static: 静态角色直接映射主体
- dynamic: 动态角色使用自定义角色解析程序注册
-
principalType: string; 主体类型,必填
- APP
- USER
- ROLE
-
property: string或string数组;指定给定模型上的属性/方法/关系。 它进一步限制了ACL适用的位置。例如:
- 一个方法名:比如
"create"
- 一组方法名:比如
["create", "update"]
- 一个方法名:比如
Scopes
_Scopes_使您可以指定可以作为模型上的方法调用引用的常用查询。
scopes
键为模型定义一个或多个作用域(命名查询)。作用域将名称映射到模型的find()
方法使用的预定义过滤器对象; 例如:
"scopes": {
"vips": {"where": {"vip": true}},
"top5": {"limit": 5, "order": "age"}
}
上面的代码片段为模型定义了两个命名查询:
- vips:查找vip标志设置为true的所有模型实例
- top5:查找按年龄排序的前五个模型实例
在scopes对象中,键是名称,每个值定义[PersistedModel.find()]的过滤器对象
您还可以使用模型的scope()
方法以编程方式定义范围,例如:
User.scope('vips', {where: {vip: true}});
User.scope('top5', {limit: 5, order: 'age'});
现在您可以调用scope定义的方法; 例如:
User.vips(function(err, vips) {
//...
});
默认scope
如果希望将范围应用于模型的所有查询,则可以使用模型本身的默认范围。
{
"name": "Product",
"properties": {
...
},
"scope": {
"order": "name",
"limit": 100,
"where": {
"deleted": false
}
}
}
现在,将应用任何带有查询参数的CRUD操作在默认scope运行; 例如,假设上述范围,查找操作如:
Product.find({offset: 0}, cb);
等价于:
Product.find({order: "name", offset: 0, limit: 100, where: {deleted: false}}, cb)
where查询条件的默认scope
将“scope”添加到模型定义(在model.json文件中)会自动将一个方法添加到名为defaultScope()
的模型中.LoopBack将在创建,更新或查询模型时调用此方法。
每次创建或更新模型实例时,生成的defaultScope()
方法将修改与where
过滤器匹配的模型属性,以强制执行指定的值。
如果您不希望以这种方式应用默认scope,请尽可能使用命名scope。
例如,如果必须使用默认scope,但不希望它影响upsert()
,只需在调用upsert()
之前覆盖模型的defaultScope()
方法; 例如:
var defaultScope = Report.defaultScope;
Report.defaultScope = function(){};
Report.upsert({id: reportId, 'deleted': true}, function(...) {
Report.defaultScope = defaultScope;
//...
});
Methods
methods
键为模型定义[remote methods](Remote-methods.html).Its value是一个对象,每个远程方法名称都有一个字符串键:
- 实例方法名称必须以
prototype
开头。 - 静态方法名称可以是任何[合法名称](Valid-names-in-LoopBack.html)。
例如,以下定义了一个名为“greet”的静态远程方法和一个名为“getProfile”的实例方法。
...
"methods": {
"greet": {
"accepts": [
{
"arg": "msg",
"type": "string",
"http": {
"source": "query"
}
}
],
"returns": {
"arg": "greeting",
"type": "string"
},
"http": {
"verb": "get"
}
},
"prototype.getProfile": {
... // Instance remote method - options
}
...
远程方法options
注册远程方法时需要指定远程方法options,如果在代码中注册远程方法,options是Model.remoteMethod()
的参数;如果是在JSON中注册远程方法,则需要在methods
指定options,无论哪种方式,它都是具有相同属性集的JavaScript对象。
所有选项属性都是可选的。 但是,如果远程方法需要参数,则必须指定接受; 如果远程方法返回值,则必须指定返回值。
- accepts: 定义远程方法接受该映射到您定义的静态方法的参数。 对于上面的示例,函数签名是
Person.greet(name, age, callback)...
所以name是第一个参数,age是第二个参数,LoopBack自动提供回调(不要在accept数组中指定它)。
{ ...
accepts: [
{arg: 'name', type: 'string'},
{arg: 'age', type: 'number'},
...],
... }
- accessScopes: 定义访问Scopes。 仅当用户的访问令牌被授予“accessScopes”列表定义的至少一个Scopes时,才允许用户调用此远程方法。默认值是具有单个范围DEFAULT的列表。
accessScopes: [
'read',
'read:user'
]
-
description: 该方法的文本描述,由API文档生成器(如OpenAPI(以前称为Swagger))使用。 如果需要,您可以在数组中放入长字符串(请参阅下面的注释)。
-
http: 指定有关公开方法的路由的信息。 请参阅下面的http属性。
-
documented: 如果设置为false,则此方法将不会出现在生成的OpenAPI(以前称为Swagger)文档中。
-
returns: 描述远程方法的回调参数; 请参阅参数说明。 假设错误的参数; 不要指明。 如果没有提供,则默认为空数组[]。
returns: {arg: 'greeting',
type: 'string'}
http属性
http属性提供有关公开远程方法的HTTP路由的信息。
- path: 公开方法的HTTP路径(相对于模型)。
http: {path: '/sayhi'}
- verb: 方法可用的HTTP方法
- get
- post (default)
- patch
- put
- del
- all
http: {path: '/sayhi',
verb: 'get'}
- status: 在没有错误的情况下调用回调时设置的默认HTTP状态。
http: {status: 201}
- errorStatus: 调用带有错误的回调时设置的默认HTTP状态。
http:{errorStatus:400}
参数说明
接受和返回选项属性将单个参数定义为对象,或将有序参数集定义为数组。 下表描述了每个参数的属性。
- arg: string; 参数名称
- description: String or Array
- http: Object or Function; 描述从HTTP请求到参数值的映射的函数或对象。请参阅下面的输入参数的HTTP映射。
- http.target:string; 将回调参数值映射到HTTP响应对象。 支持以下值:
- status: 将res.statusCode设置为提供的值
- header: 将http.header或arg命名标头设置为该值
- required: boolean; 参数为必填
- root: boolean; 如果函数有一个回调参数用作返回给远程调用者的根对象,则将此属性设置为true。 否则返回的根对象是一个map(参数名为argument-value)。
- type: string;参数数据类型; 必须是Loopback类型。 另外,回调参数允许特殊类型“file”; 见下文。
- default: String; 将用于填充loopback-explorer输入字段和swagger文档的默认值。 注意:如果参数不存在,则不会将此值传递给远程方法函数。
- documented: boolean;如果设置为false,则此参数将不会出现在生成的OpenAPI(以前称为Swagger)文档中。
例如,指定为对象的单个参数:
{arg: 'myArg', type: 'number'}
多个参数,指定为数组:
[
{arg: 'arg1', type: 'number', required: true},
{arg: 'arg2', type: 'array'}
]
返回文件(流)响应
为回调参数指定{type:‘file’,root:true},该回调参数将作为响应正文直接发送。 文件参数可以设置为以下值之一:
- string
- buffer
- ReadableStream(任何暴露.pipe()方法的东西)
例如
module.exports = function(MyModel) {
MyModel.download = function(cb) {
// getTheStreamBody() can be implemented by calling http.request() or fs.readFile() for example
getTheStreamBody(function(err, stream) {
if (err) return cb(err);
// stream can be any of: string, buffer, ReadableStream (e.g. http.IncomingMessage)
cb(null, stream, 'application/octet-stream');
});
};
MyModel.remoteMethod('download', {
returns: [
{arg: 'body', type: 'file', root: true},
{arg: 'Content-Type', type: 'string', http: { target: 'header' }}
]
});
};
输入参数的HTTP映射
有两种方法可以为输入参数指定HTTP映射(方法接受的方式):
- 提供具有source属性的对象
- 指定自定义映射功能
使用具有source属性的对象
要使用第一种方法为输入参数指定HTTP映射,请为对象提供源属性,该属性具有下表中显示的值之一。
- body: 整个请求体作为值使用
- form: 在请求体中的表单
- query: 请求中的请求字符串
- path: 请求参数中的路由参数
- req: http请求对象
- res: http应答对象
- context: 包含请求应答的上下文对象
例如,一个参数将整个请求体作为值:
{ arg: 'data', type: 'object', http: { source: 'body' } }
显示Express HTTP请求和响应对象的另一个示例:
[
{arg: 'req', type: 'object', 'http': {source: 'req'}},
{arg: 'res', type: 'object', 'http': {source: 'res'}}
]
使用自定义映射功能
为输入参数指定HTTP映射的第二种方法是指定自定义映射函数; 例如:
{
arg: 'custom',
type: 'number',
http: function(ctx) {
// ctx is LoopBack Context object
// 1\. Get the HTTP request object as provided by Express
var req = ctx.req;
// 2\. Get 'a' and 'b' from query string or form data and return their sum.
return -req.param('a') - req.param('b');
}
}
如果未指定映射,LoopBack将按如下方式确定值(假设name为要解析的输入参数的名称):
- 如果存在具有JSON内容的HTTP请求参数args,则它使用args [‘name’]的值。
- 否则,它使用req.param(‘name’)。
####在JSON字段之外返回数据
使用arg属性指定return参数将自动返回一个JSON对象,并将您的数据存储在同名字段中。
如果要将数据作为主响应(例如数组)返回,可以通过在返回对象中设置root属性并省略arg来实现。
returns: {type: 'array', root: true}
Indexes
即使使用像MongoDB这样的NoSQL数据源连接器,也不会自动为您创建索引。 您必须运行automigrate或autoupdate才能创建索引!
使用索引属性声明模型的索引,例如:
"indexes": {
"name_age_index": {
"keys": {"name": 1, "age": -1}
},
"age_index": {"age": -1}
}
上面的代码片段为声明模型创建了两个索引:
- 名为name_age_index的复合索引,带有两个键:名称按升序排列,年龄按降序排列。
- 一个名为age_index的简单索引,带有一个键:age按降序排列。
单个索引的完整语法是:
"<indexName>": {
"keys": {
"<key1>": 1,
"<key2>": -1
},
"options": {
"unique": true
}
}
键值1指定升序,-1指定降序。
如果您不需要指定任何选项,则可以使用缩写形式:
"<indexName>": {
"<key1>": 1,
"<key2>": -1
}
您也可以在模型属性级别指定索引,例如:
{
"name": {
"type": "String",
"index": true
},
"email": {
"type": "String",
"index": {
"unique": true
}
},
"age": "Number"
}
此示例创建两个索引:一个用于名称密钥,另一个用于电子邮件密钥。 电子邮件索引是唯一的。
MySQL indexes
对于MySQL,您可以按如下方式声明多列索引(例如):
...
"indexes":
{
"UNIQUE_INDEX": {
"columns": "`column1`,`column2`,...",
"kind": "unique"
}
},
...