Ниже представлен .cpp файл с асинхронными функциями для unreal engine 5.4. Функции выполняют запросы к серверу при помощи GET, POST, PUT. Они вроде как работают верно, но мне нужно чтобы они возвращали в blueprint не просто одну string переменную, а все типы данных в отдельных переменных, которые возвращаются в ответе сервера. Ещё проверь сам код, возможно требуется его оптимизировать или как ещё говорят - рефакторинг.

Пример ответа сервера: {"id":1,"fullname":"string","gender":"male","dataOfBirth":"2024-11-01T11:37:37.702Z","preferedLanguage":"english","email":"string","phone":"string"}

#include "AsyncProfileRequests.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Json.h"
#include "JsonUtilities.h"

// GetUserProfile
UAsyncProfileRequests* UAsyncProfileRequests::GetUserProfile(const FString& ServerURL, const FString& AuthToken)
{
	UAsyncProfileRequests* Action = NewObject<UAsyncProfileRequests>();
	Action->ExecuteGetUserProfile(ServerURL, AuthToken);
	return Action;
}

void UAsyncProfileRequests::ExecuteGetUserProfile(const FString& ServerURL, const FString& AuthToken)
{
	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
	Request->OnProcessRequestComplete().BindUObject(this, &UAsyncProfileRequests::OnGetUserProfileResponseReceived);
	Request->SetURL(ServerURL + "/profile");
	Request->SetVerb("GET");
	Request->SetHeader("Content-Type", "application/json");
	Request->SetHeader("Authorization", "Bearer " + AuthToken); // Добавление Bearer токена

	Request->ProcessRequest();
}

void UAsyncProfileRequests::OnGetUserProfileResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
	if (!bWasSuccessful || !Response.IsValid() || Response->GetResponseCode() != 200)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get user profile. Status code: %d"), Response->GetResponseCode());
		OnGetUserProfileFailed.Broadcast();
		return;
	}

	TSharedPtr<FJsonObject> JsonResponse;
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
	if (FJsonSerializer::Deserialize(Reader, JsonResponse) && JsonResponse.IsValid())
	{
		UE_LOG(LogTemp, Log, TEXT("Successfully retrieved user profile: %s"), *Response->GetContentAsString());
		OnGetUserProfileSuccess.Broadcast(Response->GetContentAsString());
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to parse user profile response."));
		OnGetUserProfileFailed.Broadcast();
	}
}

// PostUserProfile
UAsyncProfileRequests* UAsyncProfileRequests::PostUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken)
{
	UAsyncProfileRequests* Action = NewObject<UAsyncProfileRequests>();
	Action->ExecutePostUserProfile(ServerURL, Fullname, Gender, DateOfBirth, PreferredLanguage, Email, Phone, AuthToken);
	return Action;
}

void UAsyncProfileRequests::ExecutePostUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken)
{
	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
	Request->OnProcessRequestComplete().BindUObject(this, &UAsyncProfileRequests::OnPostUserProfileResponseReceived);
	Request->SetURL(ServerURL + "/profile");
	Request->SetVerb("POST");
	Request->SetHeader("Content-Type", "application/json");
	Request->SetHeader("Authorization", "Bearer " + AuthToken); // Добавление Bearer токена

	TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
	JsonObject->SetStringField("fullname", Fullname);
	JsonObject->SetStringField("gender", Gender);
	JsonObject->SetStringField("dataOfBirth", DateOfBirth);
	JsonObject->SetStringField("preferedLanguage", PreferredLanguage);
	JsonObject->SetStringField("email", Email);
	JsonObject->SetStringField("phone", Phone);

	FString RequestBody;
	TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&RequestBody);
	FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);

	Request->SetContentAsString(RequestBody);
	Request->ProcessRequest();
}

void UAsyncProfileRequests::OnPostUserProfileResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
	if (!bWasSuccessful || !Response.IsValid() || Response->GetResponseCode() != 200)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to post user profile. Status code: %d"), Response->GetResponseCode());
		OnPostUserProfileFailed.Broadcast();
		return;
	}

	TSharedPtr<FJsonObject> JsonResponse;
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
	if (FJsonSerializer::Deserialize(Reader, JsonResponse) && JsonResponse.IsValid())
	{
		UE_LOG(LogTemp, Log, TEXT("Successfully posted user profile: %s"), *Response->GetContentAsString());
		OnPostUserProfileSuccess.Broadcast(Response->GetContentAsString());
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to parse post user profile response."));
		OnPostUserProfileFailed.Broadcast();
	}
}

// PutUserProfile
UAsyncProfileRequests* UAsyncProfileRequests::PutUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken)
{
	UAsyncProfileRequests* Action = NewObject<UAsyncProfileRequests>();
	Action->ExecutePutUserProfile(ServerURL, Fullname, Gender, DateOfBirth, PreferredLanguage, Email, Phone, AuthToken);
	return Action;
}

void UAsyncProfileRequests::ExecutePutUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken)
{
	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
	Request->OnProcessRequestComplete().BindUObject(this, &UAsyncProfileRequests::OnPutUserProfileResponseReceived);
	Request->SetURL(ServerURL + "/profile");
	Request->SetVerb("PUT");
	Request->SetHeader("Content-Type", "application/json");
	Request->SetHeader("Authorization", "Bearer " + AuthToken); // Добавление Bearer токена

	TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
	JsonObject->SetStringField("fullname", Fullname);
	JsonObject->SetStringField("gender", Gender);
	JsonObject->SetStringField("dataOfBirth", DateOfBirth);
	JsonObject->SetStringField("preferedLanguage", PreferredLanguage);
	JsonObject->SetStringField("email", Email);
	JsonObject->SetStringField("phone", Phone);

	// Конвертируем JSON объект в строку
	FString RequestBody;
	TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&RequestBody);
	FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);

	// Устанавливаем содержимое запроса
	Request->SetContentAsString(RequestBody);

	// Отправляем запрос
	Request->ProcessRequest();
}

void UAsyncProfileRequests::OnPutUserProfileResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
	if (!bWasSuccessful || !Response.IsValid())
	{
		// Если запрос не удался или сервер не ответил, логируем ошибку и вызываем делегат
		UE_LOG(LogTemp, Error, TEXT("PUT User Profile request failed or server did not respond."));
		OnPutUserProfileFailed.Broadcast();
		return;
	}

	// Проверяем код ответа (ожидается 200)
	if (Response->GetResponseCode() == 200)
	{
		TSharedPtr<FJsonObject> JsonResponse;
		TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
		if (FJsonSerializer::Deserialize(Reader, JsonResponse) && JsonResponse.IsValid())
		{
			// Логируем успешный результат и извлекаем данные
			UE_LOG(LogTemp, Log, TEXT("PUT User Profile successful."));

			FString Fullname = JsonResponse->GetStringField("fullname");
			FString Gender = JsonResponse->GetStringField("gender");
			FString DateOfBirth = JsonResponse->GetStringField("dataOfBirth");
			FString PreferredLanguage = JsonResponse->GetStringField("preferedLanguage");
			FString Email = JsonResponse->GetStringField("email");
			FString Phone = JsonResponse->GetStringField("phone");

			// Вызываем делегат успеха
			OnPutUserProfileSuccess.Broadcast(Response->GetContentAsString());
		}
		else
		{
			// Если не удалось десериализовать ответ, логируем и вызываем делегат ошибки
			UE_LOG(LogTemp, Error, TEXT("Failed to parse PUT User Profile response."));
			OnPutUserProfileFailed.Broadcast();
		}
	}
	else
	{
		// Логируем код ошибки и вызываем делегат ошибки
		UE_LOG(LogTemp, Error, TEXT("PUT User Profile failed with status code: %d"), Response->GetResponseCode());
		OnPutUserProfileFailed.Broadcast();
	}
}


Вот .h для этого .cpp файла:
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "AsyncProfileRequests.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProfileRequestSuccess, const FString&, ProfileData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnProfileRequestFailed);

UCLASS()
class ARSPHERE_API UAsyncProfileRequests : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

public:
	// Get User Profile (GET)
	UPROPERTY(BlueprintAssignable)
	FOnProfileRequestSuccess OnGetUserProfileSuccess;

	UPROPERTY(BlueprintAssignable)
	FOnProfileRequestFailed OnGetUserProfileFailed;

	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"), Category = "Profile")
	static UAsyncProfileRequests* GetUserProfile(const FString& ServerURL, const FString& AuthToken);

	void ExecuteGetUserProfile(const FString& ServerURL, const FString& AuthToken);

	// Post User Profile (POST)
	UPROPERTY(BlueprintAssignable)
	FOnProfileRequestSuccess OnPostUserProfileSuccess;

	UPROPERTY(BlueprintAssignable)
	FOnProfileRequestFailed OnPostUserProfileFailed;

	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"), Category = "Profile")
	static UAsyncProfileRequests* PostUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken);  // Добавлен AuthToken

	void ExecutePostUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken);  // Добавлен AuthToken

	// Put User Profile (PUT)
	UPROPERTY(BlueprintAssignable)
	FOnProfileRequestSuccess OnPutUserProfileSuccess;

	UPROPERTY(BlueprintAssignable)
	FOnProfileRequestFailed OnPutUserProfileFailed;

	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"), Category = "Profile")
	static UAsyncProfileRequests* PutUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken);  // Добавлен AuthToken

	void ExecutePutUserProfile(const FString& ServerURL, const FString& Fullname, const FString& Gender, const FString& DateOfBirth, const FString& PreferredLanguage, const FString& Email, const FString& Phone, const FString& AuthToken);  // Добавлен AuthToken

private:
	void OnGetUserProfileResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
	void OnPostUserProfileResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
	void OnPutUserProfileResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
};