后端【免费下载链接】ihp The fastest way to build type safe web apps. IHP is a new batteries-included web framework optimized for longterm productivity and programmer happiness项目地址https://gitcode.com/gh_mirrors/ih/ihp点击查看免费下载IHP 通过独立的ihp-mail包提供完整的电子邮件发送能力底层基于 Haskell 生态的mime-mail库实现。本文以 IHP 官方指南 Guide/mail.markdown 为骨架结合仓库中ihp-mail包的源码实现系统讲解如何在 IHP 项目中接入邮件功能从 Nix 依赖配置、用开发工具的 Mail Code Generator 生成模板到定制收件人/发件人/回复地址与 HSX 邮件正文再到配置 Sendmail、SMTP、AWS SES、SendGrid 等四种邮件服务器最后覆盖附件与纯文本邮件的最佳实践。读完本文你将能够在自己的 IHP 应用中独立完成注册确认邮件这类完整的事务邮件链路。认识 ihp-mailIHP 的邮件发送能力ihp-mail是 IHP 框架中负责邮件发送的独立包仓库根目录下的 ihp-mail/ihp-mail.cabal 显示其版本为 1.6.0依赖mime-mail、mime-mail-ses、smtp-mail、http-client、http-client-tls、ihp-hsx等库。该包对外暴露三个模块IHP.Mailihp-mail/IHP/Mail.hs核心模块定义了BuildMail类型类、sendMail发送函数、MailServer配置类型以及buildMail、sendWithMailServer等底层实现IHP.Mail.Typesihp-mail/IHP/Mail/Types.hs邮件相关数据类型包括MailServer、MailAttachment、SMTPEncryptionIHP.MailPreludeihp-mail/IHP/MailPrelude.hs邮件视图专用的 Prelude重新导出IHP.Mail、Network.Mail.Mime提供Address类型、IHP.ViewPrelude与IHP.Mail.Types并隐藏了html以便与邮件视图的 HSX 渲染区分。生成邮件模板时导入它即可获得编写邮件所需的全部符号。从 ihp-mail/changelog.md 可以看到ihp-mail在 v1.4.0 时从 IHP 主包中拆分为独立包v1.5.0 增加了SMTPEncryption的环境变量读取支持v1.6.0 起邮件 HTML 通过新的ihp-hsxmarkup 后端渲染并对外导出buildMail。环境准备将 ihp-mail 加入项目依赖要使用邮件功能首先需要把ihp-mail加入项目的 Haskell 依赖。IHP 项目使用 Nix 管理依赖在flake.nix的haskellPackages列表中追加即可haskellPackages p: with p; [ # Haskell dependencies go here p.ihp base wai # Add this: ihp-mail注意这是一个未闭合的列表表达式实际项目中还需按原有格式补全其余依赖并保持括号匹配。添加依赖之后执行devenv up重新构建项目或直接重启开发服务器使依赖生效。使用 Mail Code Generator 生成邮件模板IHP 的 IDE即ihp-ide包内置了邮件代码生成器可以从开发工具Development Tools中打开用于快速生成符合框架约定的邮件模板文件。生成步骤打开Mail Code Generator从下拉框中选择邮件所属的控制器Controller。例如要给新注册用户发送确认邮件就选择Users控制器在文本框中输入邮件名称例如Confirmation点击Preview预览生成结果确认无误后点击Generate。生成了什么生成完成后会在Web/Mail/Users/Confirmation.hs位置创建新的邮件模板。其默认内容为module Web.Mail.Users.Confirmation where import Web.View.Prelude import IHP.MailPrelude data ConfirmationMail ConfirmationMail { user :: User } instance BuildMail ConfirmationMail where subject Subject to ConfirmationMail { .. } Address { addressName Just F L, addressEmail fname.lnameexample.com } from hiexample.com html ConfirmationMail { .. } [hsx| Hello World |]这段模板并非手写产物而是由源码 ihp-ide/IHP/IDE/CodeGen/MailGenerator.hs 中的buildPlan函数动态拼装而成。从源码看生成器实际上会执行三个动作对应[GeneratorAction]EnsureDirectory创建Web/Mail/Users/目录CreateFile写入上述模板内容到Web/Mail/Users/Confirmation.hsAddImport自动在Web/Controller/Users.hs中追加import Web.Mail.Users.Confirmation省去手动导入。这一点在对应的单元测试 ihp-ide/Test/IDE/CodeGeneration/MailGenerator.hs 中有精确断言给定PurchaseConfirmationMail名称与Users控制器期望依次产生目录创建、文件写入和导入追加三个动作且文件内容与上述模板完全一致。此外生成器要求邮件名称与控制器名称均非空否则返回错误Neither mail name nor controller name can be empty。生成界面的表单交互控制器下拉、邮件名输入、Preview / Generate 按钮实现在 ihp-ide/IHP/IDE/CodeGen/View/NewMail.hs 中。定制邮件内容生成的模板只是一个起点接下来按业务需要逐一修改BuildMail实例的各字段。修改主题Subject将默认的Subject改为更有意义的文案subject Confirm your Account修改后邮件主题即为Confirm your Account。修改收件人To默认收件人被硬编码为fname.lnameexample.com。既然邮件发给我们的用户就应该使用用户的email字段to ConfirmationMail { .. } Address { addressName Just F L, addressEmail user.email }Address类型来自Network.Mail.Mime包含addressName显示名与addressEmail邮箱地址两个字段。因为User模型有name字段还可以把收件人显示名一并带上to ConfirmationMail { .. } Address { addressName Just user.name, addressEmail user.email }在 ihp-mail/IHP/Mail.hs 的类型类文档中to还展示了一个实用模式开发环境下把所有邮件固定发到stagingexample.com生产环境才发给真实用户避免开发时误发真实邮件。修改发件人From默认发件人被设为hiexample.com。通常你会改成自己的域名邮箱例如noreplyyourdomain.com。在源码中from的类型签名是返回Address的类方法ihp-mail/IHP/Mail.hs因此也可以写成带显示名的形式from Address { addressName Acme Inc., addressEmail hiexample.com }。修改 Reply-To 地址默认情况下邮件客户端点击回复会回复到 From 地址。如果需要把回复引导到其他邮箱可通过设置Reply-To头实现replyTo ConfirmationMail { .. } Just Address { addessName Just Support, addressEmail supportexample.com }注意源码中replyTo的默认实现返回Nothingihp-mail/IHP/Mail.hs即默认不设置该头类型是Maybe Address只有返回Just ...时才会生成Reply-To头。示例中的addessName是原文档笔误正确字段名应为addressName。编写邮件正文HTML HSX邮件正文使用 HSX 编写体验与写 IHP 视图几乎一致可以直接内联 Haskell 表达式html ConfirmationMail { .. } [hsx| Hey {user.name}, br/ Thanks for signing up! Please confirm your account by following this link: ... br /br / |]在邮件中插入链接邮件正文经常需要包含链接例如评论通知跳转到帖子详情。由于Mail 不持有ControllerContext这是设计使然邮件可以通过 IHP Script 发送而脚本运行在请求/响应生命周期之外所以类似urlTo这类依赖上下文辅助函数需要定义成同时兼容 Mail 与 Controller 两种上下文的通用签名# Application/Helper/Controller.hs redirectExample :: forall context. (?context :: context, ConfigProvider context) Text redirectExample urlTo PostsAction这里使用隐式参数?context与ConfigProvider约束使其在 Controller 和 Mail 中都能工作。定义好之后即可在邮件中使用html ConfirmationMail { .. } [hsx| Hey {user.name}, br/ p Thanks for signing up! Please confirm your account by following this link: ... /p p a href{redirectExample}Click here to view all Posts/a /p |]发送邮件在控制器或脚本中通过sendMail发送邮件。注意必须显式从IHP.Mail导入sendMailimport IHP.Mail (sendMail) action MyAction do user - fetch ... sendMail ConfirmationMail { user }底层调用链从源码 ihp-mail/IHP/Mail.hs 可以看到发送过程的内部实现sendMail mail sendWithMailServer getMailServer (buildMail mail)先通过getMailServer从框架配置?context.frameworkConfig.appConfig的 TMap中取出邮件服务器配置再将邮件构建为Mail后交给对应后端发送getMailServer在找不到任何邮件服务器配置时会抛出错误No mail server configured. Add option Sendmail or similar to Config.hs——这提醒我们必须先配置邮件服务器见下文才能调用sendMailbuildMail负责把类型类方法汇总成一条完整邮件调用to/from/subject/text/html生成基础邮件追加cc、bcc把Subject与可选的Reply-To头写入mailHeaders再把attachments转换为(contentType, name, content)元组列表。BuildMail类型类还提供两个经常被忽略的默认空实现cc mail []与bcc mail []ihp-mail/IHP/Mail.hs需要抄送或密送时可在实例中覆盖它们返回[Address]列表。自定义邮件头Custom Headers如果还需要发送特定的邮件头可以实现headers方法headers ConfirmationMail { .. } [ (X-Mailer, mail4j 2.17.0) , (In-Reply-To, 123456list.example.com) ]实现细节同样见 ihp-mail/IHP/Mail.hs 的buildMailIHP 会先添加自己设置的头部Subject以及可选时的Reply-To然后才拼接headers返回的自定义头部。因此如果不想使用replyTo辅助方法完全可以手动在headers中加(Reply-To, ...)自定义头不会覆盖to、from、cc、bcc、subject、reply-to等框架管理的头部类型类文档对此有明确注释。配置邮件服务器Mail Servers要真正发出邮件必须在Config/Config.hs中配置一个邮件服务器。IHP 支持四种方式本地sendmail、AWS Simple Email ServiceSES、SendGrid可直接或经 Azure 使用以及任意标准 SMTP 服务器。对应数据类型定义在 ihp-mail/IHP/Mail/Types.hs 的MailServer中SES、Sendmail、SendGrid、SMTP四个构造子。需要提醒邮件的成功送达在很大程度上取决于发件域名是否允许你的邮件服务器发送即 SPF 和/或 DKIM 配置。请查阅你所用邮件服务商的文档完成相关设置这属于邮件基础设施层面的必修课。使用本地 Sendmail最简单的配置是使用本机sendmail-- Config/Config.hs import IHP.Mail.Types config :: ConfigBuilder config do -- other options here, then add: option Sendmail底层实现中sendWithMailServer Sendmail会把邮件渲染为 RFC 标准文本renderMail然后交给本机sendmail二进制处理ihp-mail/IHP/Mail.hs。使用 SMTP-- Config/Config.hs import IHP.Mail.Types config :: ConfigBuilder config do -- other options here, then add: option $ SMTP { host smtp.myisp.com , port 2525 , credentials Nothing -- or: Just (myusername,hunter2) , encryption TLS -- -- other options: Unencrypted or STARTTLS }SMTP构造子包含四个字段ihp-mail/IHP/Mail/Types.hshost服务器地址、port端口类型为PortNumber、credentials可选的用户名/密码二元组、encryption加密方式。SMTPEncryption只有三个合法取值Unencrypted、TLS、STARTTLS。从 ihp-mail/IHP/Mail.hs 的sendWithMailServer SMTP实现可以看出该配置会按是否有凭证与加密方式组合出六种底层调用无凭证时分别走sendMail明文、sendMailTLSTLS或sendMailSTARTTLSSTARTTLS有凭证时则对应三个带登录的变体sendMailWithLogin/sendMailWithLoginTLS/sendMailWithLoginSTARTTLS。用环境变量配置 SMTP推荐把 SMTP 凭证硬编码在代码里不是好习惯。改用环境变量后本地开发与云端生产如 AWS可以使用完全不同的凭据。下面是官方推荐写法它同时兼容 MailHog 等本地邮件测试工具-- Config/Config.hs -- Add these imports import IHP.Mail.Types import Network.Socket (PortNumber) import IHP.EnvVar config :: ConfigBuilder config do -- Previous options -- ... -- Read SMTP configuration from environment variables. smtpHost - env Text SMTP_HOST smtpPort - env PortNumber SMTP_PORT smtpEncryption - env SMTPEncryption SMTP_ENCRYPTION smtpUserMaybe - envOrNothing SMTP_USER smtpPasswordMaybe - envOrNothing SMTP_PASSWORD -- Determine if credentials are available and set SMTP configuration accordingly. let smtpCredentials case (smtpUserMaybe, smtpPasswordMaybe) of (Just user, Just password) - Just (user, password) _ - Nothing -- Print out SMTP configuration for debugging purposes. liftIO $ putStrLn $ SMTP HOST: show smtpHost liftIO $ putStrLn $ SMTP PORT: show smtpPort -- SMTP to work with MailHog or other SMTP services. option $ SMTP { host cs smtpHost , port smtpPort , credentials smtpCredentials , encryption smtpEncryption }这里用到了IHP.EnvVar模块ihp/IHP/EnvVar.hs的env与envOrNothing前者在环境变量缺失或格式非法时直接报错后者返回Maybe以便优雅降级。SMTPEncryption之所以能直接通过env SMTPEncryption读取是因为 ihp-mail/IHP/Mail/Types.hs 为其实现了EnvVarReader实例只接受Unencrypted、TLS、STARTTLS三个字符串值其他值返回Should be set to Unencrypted, TLS, or STARTTLS错误。然后在项目的.envrc文件中设置这些变量# Add your env vars here # # E.g. export AWS_ACCESS_KEY_IDXXXXX export SMTP_HOST127.0.0.1 # On some computers may need 127.0.1.1 instead. export SMTP_PORT1025 export SMTP_ENCRYPTIONUnencrypted当应用部署到云端例如 AWS 上使用 SES时则把这些变量写进flake.nix的services.ihp.additionalEnvVarsservices.ihp { domain ...; migrations ./Application/Migration; schema ./Application/Schema.sql; fixtures ./Application/Fixtures.sql; sessionSecret ...; additionalEnvVars { SMTP_HOST email-smtp.eu-west-1.amazonaws.com; SMTP_PORT 587; SMTP_ENCRYPTION STARTTLS; SMTP_USER your-user-name; SMTP_PASSWORD your-password; ENV_NAME qa; AWS_ACCESS_KEY_ID your key ID; AWS_SECRET_ACCESS_KEY your secret; }; };本地 SMTP MailHog 联调MailHog 是一款本地邮件测试工具会截获所有外发邮件并在网页上展示其 HTML非常适合开发阶段检查邮件效果。配置步骤确保本机已安装并配置好sendmail安装 MailHog按上面的环境变量配置写好Config/Config.hs与.envrc启动 MailHog打开 http://0.0.0.0:8025/通过应用发送一封邮件即可在 MailHog 中看到它。使用 AWS SES-- Config/Config.hs import IHP.Mail.Types config :: ConfigBuilder config do -- other options here, then add: option $ SES { accessKey YOUR AWS ACCESS KEY , secretKey YOUR AWS SECRET KEY , region eu-west-1 -- YOUR REGION }从源码看SES 后端会走 HTTP 通道sendWithMailServer SES创建基于 TLS 的 HTTP 管理器Network.HTTP.Client.TLS.tlsManagerSettings构造Mailer.SES记录包含发件人、收件人列表——mailTo mailCc mailBcc、accessKey、secretKey、region然后通过mime-mail-ses的renderSendMailSES调用 AWS SES APIihp-mail/IHP/Mail.hs。使用 SendGrid-- Config/Config.hs import IHP.Mail.Types config :: ConfigBuilder config do -- other options here, then add: option $ SendGrid { apiKey YOUR SENDGRID API KEY , category Nothing -- or Just mailcategory }SendGrid构造子包含apiKey与可选的category字段。源码实现显示当category为Just时会向邮件头写入X-SMTPAPI头内容为{category: 分类名}随后通过smtp.sendgrid.net:587端口、以apikey为用户名、以你的 API Key 为密码调用sendMailWithLoginSTARTTLS完成发送ihp-mail/IHP/Mail.hs。邮件附件Email Attachments通过attachments方法为邮件添加附件module Web.Mail.Users.Confirmation where import Web.View.Prelude import IHP.MailPrelude data ConfirmationMail ConfirmationMail { user :: User } instance BuildMail ConfirmationMail where subject Subject to ConfirmationMail { .. } Address { addressName Just F L, addressEmail fname.lnameexample.com } from hiexample.com html ConfirmationMail { .. } [hsx| Hello World |] attachments ConfirmationMail { .. } [ MailAttachment { name attachment.xml, contentType application/xml, content xml.../xml } ]MailAttachment数据类型定义在 ihp-mail/IHP/Mail/Types.hs包含三个字段name文件名、content文件内容类型为LByteString、contentTypeMIME 类型。类型类中attachments的默认实现返回空列表覆盖它即可返回[MailAttachment]。纯文本邮件Plain Text Emails考虑到部分邮件客户端只支持纯文本每封邮件最好都提供对应的纯文本版本。如果只设置了html而没有textIHP 会自动生成纯文本版本——方式是把 HTML 的所有标签剥离stripTags。类型类的默认实现为text mail stripTags (renderMarkupText (html mail))ihp-mail/IHP/Mail.hs但文档明确评价这是suboptimal次优方案因为剥掉标签后得到的文本往往语义不清。更好的做法是手动编写有意义的纯文本版本text ConfirmationMail { .. } cs [trimming| Hey ${userName}, Thanks for signing up! Please confirm your account by following this link: https://.... |] where userName user.name与html版本相比这里有三个关键差异使用[trimming| ... |]而非[hsx| ... |]因此不能使用 HSX 的内联 Haskell 语法如{user.userName}只能做简单的文本替换注意替换符带美元符号${userName}[trimming||]准引用器会自动去除缩进引入的空白这些缩进空白在最终邮件文本中是不需要的trimming会帮我们清理干净使用cs做类型转换把[Char]字符串字面量转换为text方法要求的Text类型。在真实业务场景中组合使用邮件能力通常与认证、后台任务、脚本等特性配合出现仓库文档中有大量现成范例可以对照学习注册确认邮件Guide/authentication.markdown 展示了完整的确认邮件流程——创建Web/Mail/Users/ConfirmationMail.hsinstance BuildMail (ConfirmationMail User)在CreateUserAction创建用户后调用sendConfirmationMail user并可选在afterConfirmation钩子中通过sendMail WelcomeMail { user }发送欢迎邮件密码重置邮件同文档还给出了PasswordResetMail { user, token }的完整模板利用urlTo (EditPasswordResetAction user.id token)在邮件中嵌入带 token 的重置链接定时批量邮件Guide/jobs.markdown 展示了如何在后台 Job 中sendMail (MarketingMail customer)逐个给客户发营销邮件适合与 cron 结合做周期任务脚本发送Guide/scripts.markdown 说明 IHP Script 可在请求/响应生命周期之外运行并发送邮件这正是 Mail 不携带ControllerContext的根本原因适合做邮件提醒等批处理。小结IHP 的邮件功能链路清晰ihp-mail包提供类型安全的BuildMail实例与四种邮件后端Mail Code Generator 一键生成符合约定的模板sendMail从框架配置读取邮件服务器后完成投递。实践要点可归纳为三条一是把 SMTP 凭证交给环境变量管理本地配合 MailHog 联调、云端写入flake.nix的additionalEnvVars二是每封邮件都手动提供纯文本版本而非依赖标签剥离三是注意 Mail 上下文与 Controller 上下文的不同跨上下文使用的 URL 辅助函数需要写成携带?context隐式参数的通用签名。结合 Guide/config.markdown 中必须配置 MailServer 才能调用 sendMail的约定以及 ihp-mail/IHP/Mail.hs 中各后端的实现细节你可以在 IHP 应用中稳定地构建完整的邮件收发体系。赞分享后端【免费下载链接】ihp The fastest way to build type safe web apps. IHP is a new batteries-included web framework optimized for longterm productivity and programmer happiness项目地址https://gitcode.com/gh_mirrors/ih/ihp点击查看免费下载相关推荐IHP邮件系统集成发送与接收邮件完全教程IHP邮件系统集成发送与接收邮件完全教程 IHP框架提供了完整的邮件系统集成功能让开发者能够轻松地在Web应用中发送和接收邮件。本教程将带你从零开始掌握I后端WindowsInternals内存管理工具详解MemInfo与MemLimit的实战应用WindowsInternals内存管理工具详解MemInfo与MemLimit的实战应用 Windows Internals内存管理工具套件提供了强大的系统示例工程Tabby 怎么配置 SMTP 邮件投递并发送测试邮件验证Tabby 怎么配置 SMTP 邮件投递并发送测试邮件验证 Tabby 是一个自托管的 AI 编程助手其中密码重置password reset、邮件通知人工智能大模型本地部署模型推理服务后端RAG交互助手上一篇BrowserAct 命令完全索引面向 AI Agent 的浏览器自动化 CLI 命令速查与实战编排下一篇3个技巧解锁Gofile下载器批量下载、断点续传与智能配置实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考