8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Micronaut GraalVM Sendgrid AWS lambda 应用程序在尝试发送电子邮件时不断从 Sendgrid 返回 400

Kevin Bott 1月前

18 0

我使用 micronaut、java 和 sendgrid 构建了一个简单的电子邮件应用程序。将其用作部署到 AWS 的基本 java 应用程序,电子邮件 sendfine。我创建了另一个 lambda 来尝试使用

我使用 micronaut、java 和 sendgrid 构建了一个简单的电子邮件应用程序。将其用作部署到 AWS 的基本 java 应用程序,电子邮件发送正常。我创建了另一个 lambda 来尝试使用 GraalVM 功能。我只是遵循了 Micronaut 文档 guid,一切都编译成功,并使用 ./gradlew buildNativeLambda 进行构建。在 AWS 控制台中运行测试选项可以正常工作,并且应用程序不会返回任何错误,但是 sendgrid 返回了 400。我觉得我缺少了一些基本的东西。我尝试将我能想到的所有内容添加到 resources/META-INF/native-image/reflect-config.json 中。尝试了 micronaut 文档中显示的不同方法来运行 lambda,即使用 Controller 方法、FunctionRequestHandler 和 FunctionLambdaRuntime。使用每种方法,lambda 都可以工作,并且它会返回字符串或 APIGatewayProxyResponseEvent,没有问题。似乎在构建电子邮件时出现了一些问题,我感觉我只是在墙上乱扔垃圾,但什么都没粘住,无论我做何改变,我都不会收到新的错误代码或任何可以推动我朝着正确方向发展的东西。

  • 当前的 build.gradle - 此时它已经非常臃肿,但就像我说的,我一直在努力让它工作,所以我似乎只是不断地添加东西,希望能有所改变
    plugins {
        id("com.github.johnrengelman.shadow") version "8.1.1"
        id("io.micronaut.application") version "4.4.2"
        id("com.diffplug.spotless") version "6.23.3"
        id("io.micronaut.aot") version "4.4.2"
    }
    
    version = "0.1"
    group = "example.micronaut"
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        annotationProcessor("io.micronaut:micronaut-http-validation")
        annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
        implementation("io.micronaut.email:micronaut-email-sendgrid")
        implementation("io.micronaut:micronaut-http-client-jdk")
        implementation("jakarta.mail:jakarta.mail-api:2.1.3")
        implementation("io.micronaut.aws:micronaut-aws-lambda-events-serde")
        implementation("io.micronaut.serde:micronaut-serde-jackson")
        runtimeOnly("org.yaml:snakeyaml")
        runtimeOnly("ch.qos.logback:logback-classic")
    }
    
    
    application {
        mainClass = "example.micronaut.Application"
    }
    java {
        sourceCompatibility = JavaVersion.toVersion("17")
        targetCompatibility = JavaVersion.toVersion("17")
    }
    
    shadowJar {
        // Ensure resources are included
        mergeServiceFiles()
        include 'EmailTemplate/**'
    }
    
    sourceSets {
        main {
            resources {
                srcDirs = ['src/main/resources']
    //            include([ '**/*.properties', '**/*.yml', '**/*.json', '**/*.png', '**/*.html', '**/*.css','**/*.JPG'])
            }
        }
    }
    
    graalvmNative {
        toolchainDetection = false
    
        binaries {
            main {
                javaLauncher = javaToolchains.launcherFor {
                    languageVersion = JavaLanguageVersion.of(17)
                    vendor = JvmVendorSpec.matching("GraalVM Community")
                }
                resources.autodetect()
                metadataRepository { enabled = true }
                imageName.set('graal-mail')
                buildArgs.add('--verbose')
                buildArgs.add('--initialize-at-build-time=kotlin.coroutines.intrinsics.CoroutineSingletons')
                buildArgs.add('--initialize-at-run-time=reactor.core.publisher.Traces$StackWalkerCallSiteSupplierFactory')
                buildArgs.add('--initialize-at-run-time=reactor.core.publisher.Traces$ExceptionCallSiteSupplierFactory')
            }
        }
    }
    
    micronaut {
        runtime("lambda_provided")
        testRuntime("junit5")
        processing {
            incremental(true)
            annotations("example.micronaut.*")
        }
        aot {
            // Please review carefully the optimizations enabled below
            // Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
            optimizeServiceLoading = false
            convertYamlToJava = false
            precomputeOperations = true
            cacheEnvironment = true
            optimizeClassLoading = true
            deduceEnvironment = true
            optimizeNetty = true
            replaceLogbackXml = true
        }
    }
    
    
    tasks.named("dockerfileNative") {
        baseImage = "amazonlinux:2023"
        jdkVersion = "17"
        args(
                "-XX:MaximumHeapSizePercent=80",
                "-Dio.netty.allocator.numDirectArenas=0",
                "-Dio.netty.noPreferDirect=true"
        )
    }
    
    spotless {
        java {
            licenseHeaderFile(file("LICENSEHEADER"))
        }
    } 
  • 电子邮件生成器——此时我再次将注释放在可能不需要的地方,但似乎没有什么影响它,应用程序运行,但从 lambda GraalVM 发送电子邮件无法正确发送
    package example.micronaut.services
    
    import com.sendgrid.Response;
    import example.micronaut.Util.MimeType;
    import example.micronaut.Util.UtilMailService;
    import io.micronaut.context.annotation.Value;
    import io.micronaut.core.annotation.ReflectiveAccess;
    import io.micronaut.email.BodyType;
    import io.micronaut.email.Email;
    import io.micronaut.email.sendgrid.SendgridEmailSender;
    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;
    import jakarta.mail.internet.MimeBodyPart;
    
    import java.util.Optional;
    import java.util.concurrent.atomic.AtomicInteger;
    
    @Singleton
    @ReflectiveAccess
    public class TestCampaign {
        private final UtilMailService utilMailService;
        private final SendgridEmailSender sendgridEmailSender;
    
        @Value("${micronaut.email.from.email}")
        private String fromEmail;
    
        @Inject
        public TestCampaign(SendgridEmailSender sendgridEmailSender, UtilMailService utilMailService) {
            this.sendgridEmailSender = sendgridEmailSender;
            this.utilMailService = utilMailService;
        }
    
        public Response sendTestEmail() throws Exception {
            AtomicInteger index = new AtomicInteger(0);
            Email.Builder emailBuilder = getEmailBuilder();
    
            utilMailService.getContacts("EmailTemplate/EmailListTest.json").forEach(contact -> {
                if (index.getAndIncrement() == 0) {
                    emailBuilder.to(contact);
                } else {
                    emailBuilder.bcc(contact);
                }
            });
    
            return sendgridEmailSender.send(emailBuilder.build());
        }
    
        private Email.Builder getEmailBuilder() throws Exception {
            Optional<String> bodyOption = utilMailService.readHtmlFile("EmailTemplate/StdEmail.html");
            String body = bodyOption.orElse("Be Aware of Your Prescriptions at work");
            return Email.builder()
                    .from(fromEmail)
                    .subject("subject")
                    .body(body, BodyType.HTML)
                    .attachment(utilMailService.buildAttachment("EmailTemplate/Meds1.png", "meds1.png", MimeBodyPart.ATTACHMENT, MimeType.IMAGE_PNG).build())
                    .attachment(utilMailService.buildAttachment("EmailTemplate/Meds2.png", "meds2.png", MimeBodyPart.ATTACHMENT, MimeType.IMAGE_PNG).build())
                    .attachment(utilMailService.buildAttachment("EmailTemplate/Meds3.JPG", "meds3.JPG", MimeBodyPart.ATTACHMENT, MimeType.IMAGE_JPEG).build())
                    .attachment(utilMailService.buildAttachment("EmailTemplate/AllMeds.png", "AllMeds.png", MimeBodyPart
  • 当前实用程序可从 json 文件、附件和资源中的 html 页面添加联系人
    package example.micronaut.Util;
    
    import io.micronaut.core.annotation.NonNull;
    import io.micronaut.core.annotation.Nullable;
    import io.micronaut.core.annotation.ReflectiveAccess;
    import io.micronaut.core.io.IOUtils;
    import io.micronaut.core.io.ResourceResolver;
    import io.micronaut.core.type.Argument;
    import io.micronaut.email.Attachment;
    import io.micronaut.email.Contact;
    import io.micronaut.serde.ObjectMapper;
    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;
    import jakarta.mail.internet.MimeBodyPart;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.util.*;
    
    @Singleton
    @ReflectiveAccess
    public class UtilMailService {
        private final ResourceResolver resourceResolver;
        private final ObjectMapper objectMapper;
    
    
        @Inject
        public UtilMailService(ResourceResolver resourceResolver, ObjectMapper objectMapper) {
            this.objectMapper = objectMapper;
            this.resourceResolver = resourceResolver;
        }
    
        public @NonNull Attachment.Builder buildAttachment(String path, String name, String disposition, MimeType type) throws Exception {
            Optional<byte[]> fileBytes = getClasspathResourceAsBytes(path);
    
            if (fileBytes.isEmpty()) {
                throw new IllegalArgumentException("File not found! " + path);
            }
    
            Attachment.Builder newAttachment = Attachment.builder().filename(name).contentType(type.getMimeType()).content(fileBytes.get());
    
            if (Objects.equals(disposition, MimeBodyPart.INLINE)) {
    
                newAttachment.id(name).disposition(disposition);
            }
    
            return newAttachment;
        }
    
        public Optional<byte[]> getClasspathResourceAsBytes(String path) throws Exception {
            Optional<URL> url = resourceResolver.getResource("classpath:" + path);
            if (url.isPresent()) {
                try (InputStream inputStream = url.get().openStream()) {
                    return Optional.of(inputStream.readAllBytes());
                }
            }
            else {
                return Optional.empty();
            }
        }
    
        public List<Contact> getContacts(String path) throws IOException {
            List<Contact> contactList = new ArrayList<>();
            Map<String, String> contactMap = readJsonFileToMap(path).orElse(Map.of("[email protected]", "crash"));
            contactMap.forEach((key, value) -> {
                contactList.add(new Contact(key, value));
            });
    
            return contactList;
        }
    
        public @Nullable Optional<Map<String, String>> readJsonFileToMap(String resourcePath) throws IOException {
            Optional<URL> url = resourceResolver.getResource("classpath:" + resourcePath);
            if (url.isPresent()) {
                try (InputStream inputStream = url.get().openStream()) {
                    return Optional.of(objectMapper.readValue(inputStream.readAllBytes(), Argument.mapOf(String.class, String.class)));
                }
            }
            else {
                return Optional.empty();
            }
        }
    
        public Optional<String> readHtmlFile(String path) throws Exception {
            Optional<URL> url = resourceResolver.getResource("classpath:" + path);
            if (url.isPresent()) {
                return Optional.of(IOUtils.readText(new BufferedReader(new InputStreamReader(url.get().openStream()))));
            }
            else {
                return Optional.empty();
            }
        }
    
        public Optional<String> getClasspathResourceAsText(String path) throws Exception {
            Optional<URL> url = resourceResolver.getResource("classpath:" + path);
            if (url.isPresent()) {
                return Optional.of(IOUtils.readText(new BufferedReader(new InputStreamReader(url.get().openStream()))));
            }
            else {
                return Optional.empty();
            }
        }
    }
  • 控制器代码
package example.micronaut;

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.sendgrid.Response;
import example.micronaut.services.EmailSendingService;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.QueryValue;
import io.micronaut.serde.ObjectMapper;
import jakarta.inject.Inject;

import java.util.Collections;

@Controller
public class HomeController {
    private final EmailSendingService emailSendingService;
    @Inject
    ObjectMapper objectMapper;
    APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();

    @Inject
    public HomeController(EmailSendingService emailSendingService) {
        this.emailSendingService = emailSendingService;
    }

    @Get
    public APIGatewayProxyResponseEvent index(@QueryValue(defaultValue = "test") String campaign) {
        try {

            Response sendGridResponse = emailSendingService.sendCustomizedEmail(campaign);
            String json = new String(objectMapper.writeValueAsBytes(Collections.singletonMap("message", response.getHeaders())));
            response.setStatusCode(sendGridResponse.getStatusCode());
            response.setBody(json);
        }
        catch (Exception e) {
            response.setStatusCode(500);
            response.setBody(String.valueOf(e.getMessage()));
        }

        return response;
    }
}
  • 当前运行时配置

enter image description here

  • 测试响应

-- 执行日志

    {
      "statusCode": 200,
      "headers": {
        "Date": "Sat, 14 Sep 2024 18:05:23 GMT",
        "Content-Type": "application/json"
      },
      "multiValueHeaders": {
        "Date": [
          "Sat, 14 Sep 2024 18:05:23 GMT"
        ],
        "Content-Type": [
          "application/json"
        ]
      },
      "body": "{\"statusCode\":400,\"body\":\"{\\\"message\\\":null}\"}",
      "isBase64Encoded": false
    }

-- 日志输出

    START RequestId: b984c570-f7af-4d4c-a929-0ae8cf1fdcd7 Version: $LATEST
     [36m18:05:23.612 [0;39m  [1;30m[main] [0;39m  [34mINFO  [0;39m  [35mi.m.e.sendgrid.SendgridEmailSender [0;39m - Status Code: 400
     [36m18:05:23.612 [0;39m  [1;30m[main] [0;39m  [34mINFO  [0;39m  [35mi.m.e.sendgrid.SendgridEmailSender [0;39m - Body: {"errors":[{"message":"The from object must be provided for every email send. It is an object that requires the email parameter, but may also contain a name parameter.  e.g. {\"email\" : \"[email protected]\"}  or {\"email\" : \"[email protected]\", \"name\" : \"Example Recipient\"}.","field":"from.email","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#message.from"},{"message":"The personalizations field is required and must have at least one personalization.","field":"personalizations","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#-Personalizations-Errors"},{"message":"Unless a valid template_id is provided, the content parameter is required. There must be at least one defined content block. We typically suggest both text/plain and text/html blocks are included, but only one block is required.","field":"content","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#message.content"}]}
     [36m18:05:23.612 [0;39m  [1;30m[main] [0;39m  [34mINFO  [0;39m  [35mi.m.e.sendgrid.SendgridEmailSender [0;39m - Headers {Strict-Transport-Security=max-age=600; includeSubDomains, Server=nginx, Access-Control-Allow-Origin=https://sendgrid.api-docs.io, Access-Control-Allow-Methods=POST, Connection=keep-alive, X-No-CORS-Reason=https://sendgrid.com/docs/Classroom/Basics/API/cors.html, Content-Length=980, Access-Control-Max-Age=600, Date=Sat, 14 Sep 2024 18:05:23 GMT, Access-Control-Allow-Headers=Authorization, Content-Type, On-behalf-of, x-sg-elas-acl, Content-Type=application/json}
    END RequestId: b984c570-f7af-4d4c-a929-0ae8cf1fdcd7
    REPORT RequestId: b984c570-f7af-4d4c-a929-0ae8cf1fdcd7  Duration: 2722.27 ms    Billed Duration: 2723 ms    Memory Size: 128 MB Max Memory Used: 113 MB 

就像我说的,应用程序在未部署为 GraalVM 时也能正常工作。任何帮助或其他尝试都将不胜感激。

我尝试将我能想到的所有内容添加到 resources/META-INF/native-image/reflect-config.json 中。尝试了 micronaut 文档中显示的不同方法来运行 lambda ,即使用 Controller 方法、FunctionRequestHandler 和 FunctionLambdaRuntime。使用每种方法,lambda 都可以工作,并且它会返回字符串或 APIGatewayProxyResponseEvent,没有问题。构建电子邮件似乎有些问题

帖子版权声明 1、本帖标题:Micronaut GraalVM Sendgrid AWS lambda 应用程序在尝试发送电子邮件时不断从 Sendgrid 返回 400
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Kevin Bott在本站《java》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 我尝试简化为仅发送给 1 个人,仅使用收件人、发件人、主题和正文,将正文和其他输入更改为硬编码字符串,但出现同样的错误

返回
作者最近主题: