如何使模型 属性 在环回 4 mongoDB 中唯一?
How do I make a model property unique in loopback 4 mongoDB?
我想让 name 字段在 loopback 4 模型中是唯一的。我正在使用 MongoDB 数据源。我尝试使用 index: { unique: true }
似乎不起作用。我阅读了有关 validatesUniquenessOf()
验证的环回文档,但我不清楚如何以及在何处使用它。
@model()
export class BillingAddress extends Entity {
@property({
type: 'string',
id: true,
mongodb: {dataType: 'ObjectId'}
})
_id: string;
@property({
type: 'string',
required: true,
index: {
unique: true
},
})
name: string;
...
...
...
constructor(data?: Partial<BillingAddress>) {
super(data);
}
}
validatesUniquenessOf()
是有效的并且在旧的环回版本中可用
https://apidocs.strongloop.com/loopback-datasource-juggler/#validatable-validatesuniquenessof
在 Loopback 4 中,如文档中所述,有两种方法可以处理此类独特的约束,
1.在 ORM 层添加验证 -
https://loopback.io/doc/en/lb4/Validation-ORM-layer.html
Schema constraints are enforced by specific databases, such as unique index
因此我们需要在 mongodb
的数据库级别添加唯一约束
> db.BillingAddress.createIndex({ "name": 1 }, { unique: true })
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
创建唯一索引后,当我们尝试插入具有相同名称的帐单地址时,我们应该得到以下错误,
MongoError: E11000 duplicate key error collection: BillingAddress.BillingAddress index: name_1 dup key: { : "Bob" }
Request POST /billing-addresses failed with status code 500.
2。在 Controller 层添加验证 - https://loopback.io/doc/en/lb4/Validation-controller-repo-service-layer.html
我。在控制器中验证功能:
// create a validateUniqueBillingAddressName function and call it here
if (!this.validateUniqueBillingAddressName(name))
throw new HttpErrors.UnprocessableEntity('Name already exist');
return this.billingAddressRepository.create(billingAddress);
二。在拦截器处验证并将其注入控制器:
> lb4 interceptor
? Interceptor name: validateBillingAddressName
? Is it a global interceptor? No
create src/interceptors/validate-billing-address-name.interceptor.ts
update src/interceptors/index.ts
您可能需要在拦截器文件中编写验证逻辑 validate-billing-address-name.interceptor.ts
类似
import {
injectable,
Interceptor,
InvocationContext,
InvocationResult,
Provider,
ValueOrPromise
} from '@loopback/core';
import {repository} from '@loopback/repository';
import {HttpErrors} from '@loopback/rest';
import {BillingAddressRepository} from '../repositories';
/**
* This class will be bound to the application as an `Interceptor` during
* `boot`
*/
@injectable({tags: {key: ValidateBillingAddressNameInterceptor.BINDING_KEY}})
export class ValidateBillingAddressNameInterceptor implements Provider<Interceptor> {
static readonly BINDING_KEY = `interceptors.${ValidateBillingAddressNameInterceptor.name}`;
constructor(
@repository(BillingAddressRepository)
public billingAddressRepository: BillingAddressRepository
) { }
/**
* This method is used by LoopBack context to produce an interceptor function
* for the binding.
*
* @returns An interceptor function
*/
value() {
return this.intercept.bind(this);
}
/**
* The logic to intercept an invocation
* @param invocationCtx - Invocation context
* @param next - A function to invoke next interceptor or the target method
*/
async intercept(
invocationCtx: InvocationContext,
next: () => ValueOrPromise<InvocationResult>,
) {
try {
// Add pre-invocation logic here
if (invocationCtx.methodName === 'create') {
const {name} = invocationCtx.args[0];
const nameAlreadyExist = await this.billingAddressRepository.find({where: {name}})
if (nameAlreadyExist.length) {
throw new HttpErrors.UnprocessableEntity(
'Name already exist',
);
}
}
const result = await next();
// Add post-invocation logic here
return result;
} catch (err) {
// Add error handling logic here
throw err;
}
}
}
然后通过它的绑定键将这个拦截器注入到你的 billing-address.controller.ts
文件中,比如,
import {intercept} from '@loopback/context';
import {
repository
} from '@loopback/repository';
import {
getModelSchemaRef,
post,
requestBody
} from '@loopback/rest';
import {ValidateBillingAddressNameInterceptor} from '../interceptors';
import {BillingAddress} from '../models';
import {BillingAddressRepository} from '../repositories';
export class BillingAddressController {
constructor(
@repository(BillingAddressRepository)
public billingAddressRepository: BillingAddressRepository,
) { }
@intercept(ValidateBillingAddressNameInterceptor.BINDING_KEY)
@post('/billing-addresses', {
responses: {
'200': {
description: 'BillingAddress model instance',
content: {'application/json': {schema: getModelSchemaRef(BillingAddress)}},
},
},
})
async create(
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(BillingAddress, {
title: 'NewBillingAddress',
exclude: ['id'],
}),
},
},
})
billingAddress: Omit<BillingAddress, 'id'>,
): Promise<BillingAddress> {
return this.billingAddressRepository.create(billingAddress);
}
}
我想让 name 字段在 loopback 4 模型中是唯一的。我正在使用 MongoDB 数据源。我尝试使用 index: { unique: true }
似乎不起作用。我阅读了有关 validatesUniquenessOf()
验证的环回文档,但我不清楚如何以及在何处使用它。
@model()
export class BillingAddress extends Entity {
@property({
type: 'string',
id: true,
mongodb: {dataType: 'ObjectId'}
})
_id: string;
@property({
type: 'string',
required: true,
index: {
unique: true
},
})
name: string;
...
...
...
constructor(data?: Partial<BillingAddress>) {
super(data);
}
}
validatesUniquenessOf()
是有效的并且在旧的环回版本中可用
https://apidocs.strongloop.com/loopback-datasource-juggler/#validatable-validatesuniquenessof
在 Loopback 4 中,如文档中所述,有两种方法可以处理此类独特的约束,
1.在 ORM 层添加验证 - https://loopback.io/doc/en/lb4/Validation-ORM-layer.html
Schema constraints are enforced by specific databases, such as unique index
因此我们需要在 mongodb
的数据库级别添加唯一约束> db.BillingAddress.createIndex({ "name": 1 }, { unique: true })
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
创建唯一索引后,当我们尝试插入具有相同名称的帐单地址时,我们应该得到以下错误,
MongoError: E11000 duplicate key error collection: BillingAddress.BillingAddress index: name_1 dup key: { : "Bob" }
Request POST /billing-addresses failed with status code 500.
2。在 Controller 层添加验证 - https://loopback.io/doc/en/lb4/Validation-controller-repo-service-layer.html
我。在控制器中验证功能:
// create a validateUniqueBillingAddressName function and call it here
if (!this.validateUniqueBillingAddressName(name))
throw new HttpErrors.UnprocessableEntity('Name already exist');
return this.billingAddressRepository.create(billingAddress);
二。在拦截器处验证并将其注入控制器:
> lb4 interceptor
? Interceptor name: validateBillingAddressName
? Is it a global interceptor? No
create src/interceptors/validate-billing-address-name.interceptor.ts
update src/interceptors/index.ts
您可能需要在拦截器文件中编写验证逻辑 validate-billing-address-name.interceptor.ts
类似
import {
injectable,
Interceptor,
InvocationContext,
InvocationResult,
Provider,
ValueOrPromise
} from '@loopback/core';
import {repository} from '@loopback/repository';
import {HttpErrors} from '@loopback/rest';
import {BillingAddressRepository} from '../repositories';
/**
* This class will be bound to the application as an `Interceptor` during
* `boot`
*/
@injectable({tags: {key: ValidateBillingAddressNameInterceptor.BINDING_KEY}})
export class ValidateBillingAddressNameInterceptor implements Provider<Interceptor> {
static readonly BINDING_KEY = `interceptors.${ValidateBillingAddressNameInterceptor.name}`;
constructor(
@repository(BillingAddressRepository)
public billingAddressRepository: BillingAddressRepository
) { }
/**
* This method is used by LoopBack context to produce an interceptor function
* for the binding.
*
* @returns An interceptor function
*/
value() {
return this.intercept.bind(this);
}
/**
* The logic to intercept an invocation
* @param invocationCtx - Invocation context
* @param next - A function to invoke next interceptor or the target method
*/
async intercept(
invocationCtx: InvocationContext,
next: () => ValueOrPromise<InvocationResult>,
) {
try {
// Add pre-invocation logic here
if (invocationCtx.methodName === 'create') {
const {name} = invocationCtx.args[0];
const nameAlreadyExist = await this.billingAddressRepository.find({where: {name}})
if (nameAlreadyExist.length) {
throw new HttpErrors.UnprocessableEntity(
'Name already exist',
);
}
}
const result = await next();
// Add post-invocation logic here
return result;
} catch (err) {
// Add error handling logic here
throw err;
}
}
}
然后通过它的绑定键将这个拦截器注入到你的 billing-address.controller.ts
文件中,比如,
import {intercept} from '@loopback/context';
import {
repository
} from '@loopback/repository';
import {
getModelSchemaRef,
post,
requestBody
} from '@loopback/rest';
import {ValidateBillingAddressNameInterceptor} from '../interceptors';
import {BillingAddress} from '../models';
import {BillingAddressRepository} from '../repositories';
export class BillingAddressController {
constructor(
@repository(BillingAddressRepository)
public billingAddressRepository: BillingAddressRepository,
) { }
@intercept(ValidateBillingAddressNameInterceptor.BINDING_KEY)
@post('/billing-addresses', {
responses: {
'200': {
description: 'BillingAddress model instance',
content: {'application/json': {schema: getModelSchemaRef(BillingAddress)}},
},
},
})
async create(
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(BillingAddress, {
title: 'NewBillingAddress',
exclude: ['id'],
}),
},
},
})
billingAddress: Omit<BillingAddress, 'id'>,
): Promise<BillingAddress> {
return this.billingAddressRepository.create(billingAddress);
}
}