使用 Loopback 4 发送电子邮件
Send email with Loopback 4
我对 Loopback 和 Typescript 有点陌生,所以我不知道如何实现它。我试图直接调用 Nodemailer,但到目前为止我一直收到错误消息。
我的邮件服务:
import { SentMessageInfo } from 'nodemailer';
import Mail = require('nodemailer/lib/mailer');
const nodemailer = require("nodemailer");
export class MailerService {
async sendMail(mailOptions: Mail.Options): Promise<SentMessageInfo> {
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
auth: {
user: 'albert.grimes@ethereal.email',
pass: 'qN85JT6SneBA9S5dhy'
}
});
return await transporter.sendMail(mailOptions);
}
}
我的 Mailer 控制器:
import { Request, RestBindings, get, ResponseObject } from
'@loopback/rest';
import { inject } from '@loopback/context';
import { MailerService } from "../services";
export class MailController {
constructor(
@inject ???
public mailerService: MailerService
) { }
@get('/mail/acceptation')
async sendEmail(email: string): Promise<any> {
let info = await this.mailerService.sendMail({
to: `${email}`,
subject: 'testmail',
html: '<p>Hallo</p>'
})
return info;
}
}
我一直收到这个错误:
Unhandled error in GET /mail/acceptation: 500 Error: Cannot resolve injected arguments for MailController.prototype.sendEmail[0]: The arguments[0] is not decorated for dependency injection, but a value is not supplied
所以我从中收集到的是我应该在我的控制器中注入一个值,但我不知道是什么。
您应该有一个文件,其中存储了您的 Bindings。在那里你必须创建绑定密钥:
export namespace MailerBindings {
export const SERVICE = BindingKey.create<MailerService>('mailer.services');
}
要实际绑定服务,您必须在应用程序的构造函数中调用绑定函数:
this.bind(MailerBindings.SERVICE).toClass(MailerService);
现在您可以使用绑定注入服务了:
@inject(MailerBindings.SERVICE) public mailerService: MailerService,
https://loopback.io/doc/en/lb3/Email-connector.html 上有这方面的教程。它有一个特殊的邮件连接器,可以用作数据源。模型应该能够处理发送细节。该过程应该与您使用 lb4 cli 创建数据库连接器时几乎相同。
cli
lb4 datasource //select email
datasources.json
{
...
"myEmailDataSource": {
"connector": "mail",
"transports": [{
"type": "smtp",
"host": "smtp.private.com",
"secure": false,
"port": 587,
"tls": {
"rejectUnauthorized": false
},
"auth": {
"user": "me@private.com",
"pass": "password"
}
}]
}
...
}
型号
module.exports = function(MyModel) {
// send an email
MyModel.sendEmail = function(cb) {
MyModel.app.models.Email.send({
to: 'foo@bar.com',
from: 'you@gmail.com',
subject: 'my subject',
text: 'my text',
html: 'my <em>html</em>'
}, function(err, mail) {
console.log('email sent!');
cb(err);
});
}
};`
email.service.ts
import Utils from '../utils';
import * as nodemailer from 'nodemailer';
import { IEmail } from '../type-schema';
export interface EmailManager<T = Object> {
sendMail(mailObj: IEmail): Promise<T>;
}
export class EmailService {
constructor() { }
async sendMail(mailObj: IEmail): Promise<object> {
const configOption = Utils.getSiteOptions();
let transporter = nodemailer.createTransport(configOption.email);
return await transporter.sendMail(mailObj);
}
}
在您的配置文件中定义您的 smtp 选项,如下所示:-
"email": {
"type": "smtp",
"host": "smtp.gmail.com",
"secure": true,
"port": 465,
"tls": {
"rejectUnauthorized": false
},
"auth": {
"user": "example@gmail.com",
"pass": "sample-password"
}
}
在控制器中按以下方式发送邮件:-
import { EmailManager } from '../services/email.service';
import { EmailManagerBindings } from '../keys';
// inject in constructor
@inject(EmailManagerBindings.SEND_MAIL) public emailManager: EmailManager,
// call service method like following way
const mailOptions = {
from: configOption.fromMail,
to: getUser.email,
subject: template.subject,
html: Utils.filterEmailContent(template.message, msgOpt)
};
await this.emailManager.sendMail(mailOptions).then(function (res: any) {
return { message: `Successfully sent reset mail to ${getUser.email}` };
}).catch(function (err: any) {
throw new HttpErrors.UnprocessableEntity(`Error in sending E-mail to ${getUser.email}`);
});
简单方法:-
如果你不想做一个服务功能,只需在你的控制器中导入 nodemailer 并发送邮件,但这不是一个好方法。
import * as nodemailer from 'nodemailer';
let transporter = nodemailer.createTransport({
"type": "smtp",
"host": "smtp.gmail.com",
"secure": true,
"port": 465,
"tls": {
"rejectUnauthorized": false
},
"auth": {
"user": "example@gmail.com",
"pass": "sample-password"
}
});
return await transporter.sendMail({
from: "sender-email",
to: "receiver-email",
subject: "email-subject",
html: "message body"
});
更新:-
keys.ts
import { BindingKey } from '@loopback/context';
import { EmailManager } from './services/email.service';
import { Member } from './models';
import { Credentials } from './type-schema';
export namespace PasswordHasherBindings {
export const PASSWORD_HASHER = BindingKey.create<PasswordHasher>('services.hasher');
export const ROUNDS = BindingKey.create<number>('services.hasher.round');
}
export namespace UserServiceBindings {
export const USER_SERVICE = BindingKey.create<UserService<Member, Credentials>>('services.user.service');
}
export namespace TokenManagerBindings {
export const TOKEN_HANDLER = BindingKey.create<TokenManager>('services.token.handler');
}
export namespace EmailManagerBindings {
export const SEND_MAIL = BindingKey.create<EmailManager>('services.email.send');
}
aplication.ts
import { BootMixin } from '@loopback/boot';
import { ApplicationConfig } from '@loopback/core';
import { RepositoryMixin } from '@loopback/repository';
import { RestApplication } from '@loopback/rest';
import { ServiceMixin } from '@loopback/service-proxy';
import * as path from 'path';
import { MySequence } from './sequence';
import { TokenServiceBindings, UserServiceBindings, TokenServiceConstants, } from './keys';
import { JWTService, TokenGenerator } from './services/jwt-service';
import { EmailService } from './services/email.service';
import { MyUserService } from './services/user-service';
import { AuthenticationComponent, registerAuthenticationStrategy, } from '@loopback/authentication';
import { PasswordHasherBindings, TokenManagerBindings, EmailManagerBindings } from './keys';
import { BcryptHasher } from './services/hash.password.bcryptjs';
import { JWTAuthenticationStrategy } from './authentication-strategies/jwt-strategy';
export class AmpleServerApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication))) {
constructor(options: ApplicationConfig = {}) {
super(options);
this.setUpBindings();
// Bind authentication component related elements
this.component(AuthenticationComponent);
registerAuthenticationStrategy(this, JWTAuthenticationStrategy);
// Set up the custom sequence
this.sequence(MySequence);
// Set up default home page
this.static('/', path.join(__dirname, '../public'));
this.projectRoot = __dirname;
this.bootOptions = {
controllers: {
dirs: ['controllers'],
extensions: ['.controller.js'],
nested: true,
},
};
}
setUpBindings(): void {
this.bind(TokenServiceBindings.TOKEN_SECRET).to(TokenServiceConstants.TOKEN_SECRET_VALUE);
this.bind(TokenServiceBindings.TOKEN_EXPIRES_IN).to(TokenServiceConstants.TOKEN_EXPIRES_IN_VALUE);
this.bind(UserServiceBindings.USER_SERVICE).toClass(MyUserService);
this.bind(EmailManagerBindings.SEND_MAIL).toClass(EmailService);
}
}
我对 Loopback 和 Typescript 有点陌生,所以我不知道如何实现它。我试图直接调用 Nodemailer,但到目前为止我一直收到错误消息。
我的邮件服务:
import { SentMessageInfo } from 'nodemailer';
import Mail = require('nodemailer/lib/mailer');
const nodemailer = require("nodemailer");
export class MailerService {
async sendMail(mailOptions: Mail.Options): Promise<SentMessageInfo> {
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
auth: {
user: 'albert.grimes@ethereal.email',
pass: 'qN85JT6SneBA9S5dhy'
}
});
return await transporter.sendMail(mailOptions);
}
}
我的 Mailer 控制器:
import { Request, RestBindings, get, ResponseObject } from
'@loopback/rest';
import { inject } from '@loopback/context';
import { MailerService } from "../services";
export class MailController {
constructor(
@inject ???
public mailerService: MailerService
) { }
@get('/mail/acceptation')
async sendEmail(email: string): Promise<any> {
let info = await this.mailerService.sendMail({
to: `${email}`,
subject: 'testmail',
html: '<p>Hallo</p>'
})
return info;
}
}
我一直收到这个错误:
Unhandled error in GET /mail/acceptation: 500 Error: Cannot resolve injected arguments for MailController.prototype.sendEmail[0]: The arguments[0] is not decorated for dependency injection, but a value is not supplied
所以我从中收集到的是我应该在我的控制器中注入一个值,但我不知道是什么。
您应该有一个文件,其中存储了您的 Bindings。在那里你必须创建绑定密钥:
export namespace MailerBindings {
export const SERVICE = BindingKey.create<MailerService>('mailer.services');
}
要实际绑定服务,您必须在应用程序的构造函数中调用绑定函数:
this.bind(MailerBindings.SERVICE).toClass(MailerService);
现在您可以使用绑定注入服务了:
@inject(MailerBindings.SERVICE) public mailerService: MailerService,
https://loopback.io/doc/en/lb3/Email-connector.html 上有这方面的教程。它有一个特殊的邮件连接器,可以用作数据源。模型应该能够处理发送细节。该过程应该与您使用 lb4 cli 创建数据库连接器时几乎相同。
cli
lb4 datasource //select email
datasources.json
{
...
"myEmailDataSource": {
"connector": "mail",
"transports": [{
"type": "smtp",
"host": "smtp.private.com",
"secure": false,
"port": 587,
"tls": {
"rejectUnauthorized": false
},
"auth": {
"user": "me@private.com",
"pass": "password"
}
}]
}
...
}
型号
module.exports = function(MyModel) {
// send an email
MyModel.sendEmail = function(cb) {
MyModel.app.models.Email.send({
to: 'foo@bar.com',
from: 'you@gmail.com',
subject: 'my subject',
text: 'my text',
html: 'my <em>html</em>'
}, function(err, mail) {
console.log('email sent!');
cb(err);
});
}
};`
email.service.ts
import Utils from '../utils';
import * as nodemailer from 'nodemailer';
import { IEmail } from '../type-schema';
export interface EmailManager<T = Object> {
sendMail(mailObj: IEmail): Promise<T>;
}
export class EmailService {
constructor() { }
async sendMail(mailObj: IEmail): Promise<object> {
const configOption = Utils.getSiteOptions();
let transporter = nodemailer.createTransport(configOption.email);
return await transporter.sendMail(mailObj);
}
}
在您的配置文件中定义您的 smtp 选项,如下所示:-
"email": {
"type": "smtp",
"host": "smtp.gmail.com",
"secure": true,
"port": 465,
"tls": {
"rejectUnauthorized": false
},
"auth": {
"user": "example@gmail.com",
"pass": "sample-password"
}
}
在控制器中按以下方式发送邮件:-
import { EmailManager } from '../services/email.service';
import { EmailManagerBindings } from '../keys';
// inject in constructor
@inject(EmailManagerBindings.SEND_MAIL) public emailManager: EmailManager,
// call service method like following way
const mailOptions = {
from: configOption.fromMail,
to: getUser.email,
subject: template.subject,
html: Utils.filterEmailContent(template.message, msgOpt)
};
await this.emailManager.sendMail(mailOptions).then(function (res: any) {
return { message: `Successfully sent reset mail to ${getUser.email}` };
}).catch(function (err: any) {
throw new HttpErrors.UnprocessableEntity(`Error in sending E-mail to ${getUser.email}`);
});
简单方法:- 如果你不想做一个服务功能,只需在你的控制器中导入 nodemailer 并发送邮件,但这不是一个好方法。
import * as nodemailer from 'nodemailer';
let transporter = nodemailer.createTransport({
"type": "smtp",
"host": "smtp.gmail.com",
"secure": true,
"port": 465,
"tls": {
"rejectUnauthorized": false
},
"auth": {
"user": "example@gmail.com",
"pass": "sample-password"
}
});
return await transporter.sendMail({
from: "sender-email",
to: "receiver-email",
subject: "email-subject",
html: "message body"
});
更新:-
keys.ts
import { BindingKey } from '@loopback/context';
import { EmailManager } from './services/email.service';
import { Member } from './models';
import { Credentials } from './type-schema';
export namespace PasswordHasherBindings {
export const PASSWORD_HASHER = BindingKey.create<PasswordHasher>('services.hasher');
export const ROUNDS = BindingKey.create<number>('services.hasher.round');
}
export namespace UserServiceBindings {
export const USER_SERVICE = BindingKey.create<UserService<Member, Credentials>>('services.user.service');
}
export namespace TokenManagerBindings {
export const TOKEN_HANDLER = BindingKey.create<TokenManager>('services.token.handler');
}
export namespace EmailManagerBindings {
export const SEND_MAIL = BindingKey.create<EmailManager>('services.email.send');
}
aplication.ts
import { BootMixin } from '@loopback/boot';
import { ApplicationConfig } from '@loopback/core';
import { RepositoryMixin } from '@loopback/repository';
import { RestApplication } from '@loopback/rest';
import { ServiceMixin } from '@loopback/service-proxy';
import * as path from 'path';
import { MySequence } from './sequence';
import { TokenServiceBindings, UserServiceBindings, TokenServiceConstants, } from './keys';
import { JWTService, TokenGenerator } from './services/jwt-service';
import { EmailService } from './services/email.service';
import { MyUserService } from './services/user-service';
import { AuthenticationComponent, registerAuthenticationStrategy, } from '@loopback/authentication';
import { PasswordHasherBindings, TokenManagerBindings, EmailManagerBindings } from './keys';
import { BcryptHasher } from './services/hash.password.bcryptjs';
import { JWTAuthenticationStrategy } from './authentication-strategies/jwt-strategy';
export class AmpleServerApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication))) {
constructor(options: ApplicationConfig = {}) {
super(options);
this.setUpBindings();
// Bind authentication component related elements
this.component(AuthenticationComponent);
registerAuthenticationStrategy(this, JWTAuthenticationStrategy);
// Set up the custom sequence
this.sequence(MySequence);
// Set up default home page
this.static('/', path.join(__dirname, '../public'));
this.projectRoot = __dirname;
this.bootOptions = {
controllers: {
dirs: ['controllers'],
extensions: ['.controller.js'],
nested: true,
},
};
}
setUpBindings(): void {
this.bind(TokenServiceBindings.TOKEN_SECRET).to(TokenServiceConstants.TOKEN_SECRET_VALUE);
this.bind(TokenServiceBindings.TOKEN_EXPIRES_IN).to(TokenServiceConstants.TOKEN_EXPIRES_IN_VALUE);
this.bind(UserServiceBindings.USER_SERVICE).toClass(MyUserService);
this.bind(EmailManagerBindings.SEND_MAIL).toClass(EmailService);
}
}