Не возвращается ответ из контроллера после асинхронного выполнения 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 шт):
Автор решения: Денис Старакожев
→ Ссылка
Предположу что это связано с тем, что аутентификация привязывается к потоку, а за счет того что при исполнении метода создается новый поток, отличный от того потока в котором пришел запрос он не находит контекст запроса.
Нужно включить максимум логов и подебажить, вызывается ли у вас сам метод контроллера