Не возвращается ответ из контроллера после асинхронного выполнения Spring Boot

package com.notic.controller;

import com.notic.dto.CustomPutObjectDto;
import com.notic.projection.GetUserAvatarProjection;
import com.notic.projection.JwtAuthUserProjection;
import com.notic.service.S3Service;
import com.notic.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Callable;

@Slf4j
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    private final S3Service s3Service;

    @PostMapping("/upload-avatar")
    public Callable<ResponseEntity<String>> uploadAvatar(
            @AuthenticationPrincipal JwtAuthUserProjection principal,
            @RequestParam("file") MultipartFile file
    ) {
        return () -> {
            long userId = principal.getId();
            String key = UUID.randomUUID() + Long.toString(userId);

            try (InputStream inputStream = file.getInputStream()) {
                CustomPutObjectDto dto = new CustomPutObjectDto(
                        "noticavatar",
                        key,
                        inputStream,
                        file.getSize(),
                        file.getContentType()
                );

                Optional<GetUserAvatarProjection> avatarOptional = userService.getUserAvatarById(userId);
                if (avatarOptional.isPresent()) {
                    GetUserAvatarProjection avatar = avatarOptional.get();
                    s3Service.deleteUserAvatar(avatar.getAvatar());
                }
                String url =  s3Service.uploadUserAvatar(dto);
                userService.updateUserAvatarById(userId, url);

                return ResponseEntity.ok().body(url);
            }
        };
    }
}

s3Service

package com.notic.service;


import com.notic.dto.CustomPutObjectDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import java.net.URI;
import java.net.URISyntaxException;


@Slf4j
@Service
@RequiredArgsConstructor
public class S3Service {

    private final S3Client s3Client;

    public String uploadUserAvatar(CustomPutObjectDto dto) {
        PutObjectRequest request = PutObjectRequest.builder()
                .bucket(dto.bucket())
                .key(dto.key())
                .acl(ObjectCannedACL.PUBLIC_READ)
                .contentType(dto.contentType())
                .build();

        s3Client.putObject(request, RequestBody.fromInputStream(
                dto.dataInputStream(),
                dto.contentLength())
        );

        return "https://" + dto.bucket() + ".s3.amazonaws.com/" + dto.key();
    }

    @Async
    public void deleteUserAvatar(String avatarUrl) {
        try {
            URI uri = new URI(avatarUrl);
            String host = uri.getHost();
            String bucket = host.split("\\.")[0];
            String key = uri.getPath().substring(1);
            DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
                    .bucket(bucket)
                    .key(key)
                    .build();

            s3Client.deleteObject(deleteObjectRequest);
        } catch (URISyntaxException e) {
            log.warn(e.getMessage());
        }

    }
}

Описание проблемы:

Выполняю запрос и в результате получаю BadRequest, хотя прологировав я не обнаружил никаких ошибок, всё проходит отлично, но почему-то возвращаемого ответа нету. Это стало происходить, когда я решил сделать метод контроллера ассинхронным.


Ответы (1 шт):

Автор решения: Денис Старакожев

Предположу что это связано с тем, что аутентификация привязывается к потоку, а за счет того что при исполнении метода создается новый поток, отличный от того потока в котором пришел запрос он не находит контекст запроса.

Нужно включить максимум логов и подебажить, вызывается ли у вас сам метод контроллера

→ Ссылка