Ktor¶
Инструкция для приложений на Ktor (Kotlin, JVM). Mailexam подключается как SMTP-сервер через Jakarta Mail.
Что понадобится¶
- Аккаунт Mailexam и проект с SMTP-учётными данными.
- JDK 17+ и Kotlin 1.9+.
Скопируйте из приветственного письма (или кабинета) для вашего проекта:
ВАШ_ЛОГИН— SMTP-логин (например,xxxxx);ВАШ_ПАРОЛЬ— SMTP-пароль (уникальная пара к логину);- хост —
ВАШ_ЛОГИН.mailexam.ru(совпадает с логином).
1. Зависимости¶
Фрагмент build.gradle.kts:
plugins {
kotlin("jvm") version "2.0.0"
kotlin("plugin.serialization") version "2.0.0"
application
}
repositories {
mavenCentral()
}
dependencies {
implementation("io.ktor:ktor-server-core-jvm:3.0.0")
implementation("io.ktor:ktor-server-netty-jvm:3.0.0")
implementation("io.ktor:ktor-server-content-negotiation-jvm:3.0.0")
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:3.0.0")
implementation("com.sun.mail:jakarta.mail:2.0.1")
}
application {
mainClass.set("ApplicationKt")
}
Создайте проект через Ktor Project Generator или вручную по структуре ниже.
2. Переменные окружения¶
Перед запуском задайте переменные (или используйте .env через вашу IDE):
MAILEXAM_LOGIN=ВАШ_ЛОГИН
MAILEXAM_PASSWORD=ВАШ_ПАРОЛЬ
MAILEXAM_PORT=587
MAIL_FROM=noreply@example.test
Хост SMTP: ВАШ_ЛОГИН.mailexam.ru.
Адрес отправителя
MAIL_FROM может быть любым тестовым адресом — письмо попадёт в Mailexam, а не реальному получателю.
Альтернативные порты¶
3. Отправка почты¶
// src/main/kotlin/Mail.kt
import jakarta.mail.Authenticator
import jakarta.mail.Message
import jakarta.mail.PasswordAuthentication
import jakarta.mail.Session
import jakarta.mail.Transport
import jakarta.mail.internet.InternetAddress
import jakarta.mail.internet.MimeMessage
import java.util.Properties
object Mail {
fun sendTest(to: String, subject: String, body: String) {
val login = System.getenv("MAILEXAM_LOGIN")
?: error("MAILEXAM_LOGIN is not set")
val password = System.getenv("MAILEXAM_PASSWORD")
?: error("MAILEXAM_PASSWORD is not set")
val port = System.getenv("MAILEXAM_PORT")?.toInt() ?: 587
val from = System.getenv("MAIL_FROM") ?: "noreply@example.test"
val props = Properties().apply {
put("mail.smtp.host", "$login.mailexam.ru")
put("mail.smtp.port", port.toString())
put("mail.smtp.auth", "true")
put("mail.smtp.starttls.enable", (port == 587 || port == 2525).toString())
}
val session = Session.getInstance(props, object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication =
PasswordAuthentication(login, password)
})
val message = MimeMessage(session).apply {
setFrom(InternetAddress(from))
setRecipients(Message.RecipientType.TO, InternetAddress.parse(to))
setSubject(subject, "UTF-8")
setText(body, "UTF-8")
}
Transport.send(message)
}
}
4. Маршрут Ktor¶
// src/main/kotlin/Application.kt
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
@Serializable
data class SendRequest(
val to: String = "user@example.test",
val subject: String = "Ktor + Mailexam",
val body: String = "Тест Mailexam из Ktor",
)
fun main() {
embeddedServer(Netty, port = 8080, host = "127.0.0.1", module = Application::module)
.start(wait = true)
}
fun Application.module() {
install(ContentNegotiation) {
json()
}
routing {
post("/mail/test") {
val payload = call.receive<SendRequest>()
withContext(Dispatchers.IO) {
Mail.sendTest(
to = payload.to,
subject = payload.subject,
body = payload.body,
)
}
call.respond(mapOf("status" to "ok"))
}
}
}
Запуск и проверка:
curl -X POST http://127.0.0.1:8080/mail/test \
-H 'Content-Type: application/json' \
-d '{"to":"user@example.test","subject":"Проверка","body":"Привет"}'
Письмо появится в кабинете Mailexam → ваш проект → входящие.
5. Локальная разработка и CI¶
| Среда | Рекомендация |
|---|---|
local |
переменные окружения в Run Configuration IDE |
| CI | секреты MAILEXAM_LOGIN, MAILEXAM_PASSWORD в GitLab CI/CD Variables |
Пример для .gitlab-ci.yml:
variables:
MAILEXAM_LOGIN: $MAILEXAM_LOGIN
MAILEXAM_PASSWORD: $MAILEXAM_PASSWORD
MAILEXAM_PORT: "587"
MAIL_FROM: "noreply@example.test"
После интеграционного теста проверьте доставку через API Mailexam.
6. Типичные проблемы¶
Ошибка TLS или подключения
mail.smtp.hostдолжен быть{логин}.mailexam.ru, логин вPasswordAuthentication— из письма.- Логин и пароль — пара из письма одного проекта.
Блокировка event loop
- Вызывайте
Transport.sendвwithContext(Dispatchers.IO), как в примере.
Письмо не в кабинете
- Смотрите входящие того же проекта Mailexam.
- Проверьте логи Ktor при исключении из
Mail.sendTest.