WangShuXian6 / blog

FE-BLOG
https://wangshuxian6.github.io/blog/
MIT License
46 stars 10 forks source link

虚幻引擎 插件 / Unreal Engine Plugins #189

Open WangShuXian6 opened 8 months ago

WangShuXian6 commented 8 months ago

虚幻引擎 插件 / Unreal Engine Plugins

WangShuXian6 commented 8 months ago

UE 引入插件头文件到项目C++中

如果插件提供了C++使用的插件

配置插件 Request Source/Ro2ea/Ro2ea.Build.cs

PublicDependencyModuleNames.AddRange(new string[]
        {
            "Request"
        });

直接导入 根public的头文件即可,省略public路径

例如: 导入 Plugins/Request/Source/Request/Public/Request.h

#include "Request.h"

如果插件没有提供C++使用方式

以Request插件为例。 官方只提供了蓝图文档,根public目录没有导出有用的头文件,实际头文件均在根Private目录下。

image

必须将插件安装到项目的 Plugins 目录下, 如果安装在引擎目录下无法导入非根public的头文件

E:\Unreal Projects 532\Ro2ea\Plugins\Request

然后重新生成项目 image

Ro2ea.uproject 项目中启用插件

这将自动在 Ro2ea.uproject 的"Plugins" 加入 image

Ro2ea.Build.cs 配置插件

Source/Ro2ea/Ro2ea.Build.cs 使用 PublicDependencyModuleNames.AddRange 导入插件 Request

Source/Ro2ea/Ro2ea.Build.cs 使用 PublicIncludePaths.AddRange 导入要使用的插件目录。 "Request/Private" 表示 插件的 目录 Plugins/Request/Source/Request/Private

Source/Ro2ea/Ro2ea.Build.cs

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class Ro2ea : ModuleRules
{
    public Ro2ea(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "GameplayMessageRuntime"
            }
        );
        PublicDependencyModuleNames.AddRange(new string[]
        {
            "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "GameplayAbilities", "GameplayTags",
            "GameplayTasks", "NavigationSystem", "NavigationSystem", "Niagara", "AIModule","Request"
        });
        PublicIncludePaths.AddRange(new string[] { "Request/Public","Request/Private"});
    }
}

关键代码

        PublicDependencyModuleNames.AddRange(new string[]
        {
            "Request"
        });
        PublicIncludePaths.AddRange(new string[] { "Request/Public","Request/Private"});

项目中导入插件的 RequestHandler.h

#include"Request/Private/Handler/RequestHandler.h"

这代表导入了 Plugins/Request/Source/Request/Private/Handler/RequestHandler.h

WangShuXian6 commented 8 months ago

UE结构体和JSON字符串互转工具函数

转换原理

注意:FJsonObjectConverter::UStructToJsonObjectString 会将结构体的首字母转为小写,用以符合JSON规范,这是虚幻内置行为。后端服务通用的参数验证会导致请求返回400响应。

FJsonObjectConverter::UStructToJsonObjectString

FJsonObjectConverter::JsonObjectStringToUStruct

Source/Ro2ea/Ro2ea.Build.cs

        PublicDependencyModuleNames.AddRange(new string[]
        {
"Json", "JsonUtilities"
        });

#include "JsonObjectConverter.h"

USTRUCT(BlueprintType)
struct FPlayerData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Name;
};

void ARoCharacter::BeginPlay()
{
    Super::BeginPlay();

    // 结构体转换为json字符串
    FPlayerData PlayerData;
    PlayerData.Name = FString("PlayerOne");

    FString Content;
    FJsonObjectConverter::UStructToJsonObjectString(FPlayerData::StaticStruct(), &PlayerData, Content, 0, 0);

    UE_LOG(LogTemp, Warning, TEXT("结构体转换为json字符串: %s"), *Content);

    // json字符串转换为结构体
    FString JsonString = TEXT("{\"Name\":\"PlayerOne2222\"}");
    FPlayerData PlayerData2;

    if (FJsonObjectConverter::JsonObjectStringToUStruct<FPlayerData>(JsonString, &PlayerData2, 0, 0))
    {
        // 转换成功,PlayerData 现在被填充了 JSON 字符串中的数据
        UE_LOG(LogTemp, Warning, TEXT("json字符串转换为结构体: %s"), *PlayerData2.Name);
    }
    else
    {
        // 处理转换失败的情况
        UE_LOG(LogTemp, Warning, TEXT("json字符串转换为结构体失败"));
    }
}

封装为工具类

Source/Ro2ea/Public/User/Util/RoJson.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "JsonObjectConverter.h"
#include "RoJson.generated.h"

UCLASS()
class RO2EA_API URoJson : public UObject
{
    GENERATED_BODY()

public:
    // 将 UStruct 转换为 JSON 字符串
    template <typename StructType>
    static bool UStructToJSONString(const StructType& InStruct, FString& OutJsonString)
    {
        return FJsonObjectConverter::UStructToJsonObjectString(StructType::StaticStruct(), &InStruct, OutJsonString, 0,
                                                               0, 0, nullptr, true);
    }

    // 将 JSON 字符串转换为 UStruct
    template <typename StructType>
    static bool JSONStringToUStruct(const FString& InJsonString, StructType& OutStruct)
    {
        return FJsonObjectConverter::JsonObjectStringToUStruct<StructType>(InJsonString, &OutStruct, 0, 0, false);
    }

    // 数组
    template <typename StructType>
    static bool UStructArrayToJSONString(const TArray<StructType>& InStructArray, FString& OutJsonString)
    {
        TArray<FString> JsonStrings;
        for (const StructType& Item : InStructArray)
        {
            FString ItemJsonString;
            if (!FJsonObjectConverter::UStructToJsonObjectString(StructType::StaticStruct(), &Item, ItemJsonString, 0,
                                                                 0, 0, nullptr, true))
            {
                return false; // 转换失败,直接返回
            }
            JsonStrings.Add(ItemJsonString);
        }

        OutJsonString = FString::Printf(TEXT("[%s]"), *FString::Join(JsonStrings, TEXT(",")));
        return true;
    }

    template <typename StructType>
    static bool JSONStringToUStructArray(const FString& InJsonString, TArray<StructType>& OutStructArray)
    {
        if (!InJsonString.StartsWith("[") || !InJsonString.EndsWith("]"))
        {
            return false; // 不是有效的JSON数组格式
        }

        return FJsonObjectConverter::JsonArrayStringToUStruct<StructType>(InJsonString, &OutStructArray, 0, 0, false);
    }
};

Source/Ro2ea/Private/User/Util/RoJson.cpp

使用工具类

#include "User/Util/RoJson.h"

USTRUCT(BlueprintType)
struct FPlayerData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Name;
};

void ARoCharacter::BeginPlay()
{
    Super::BeginPlay();

    // 结构体转换为json字符串
    FPlayerData PlayerData0;
    PlayerData0.Name = TEXT("PlayerOne000");

    FString JsonString0;
    URoJson::UStructToJSONString(PlayerData0, JsonString0);

    // json字符串转换为结构体
    FPlayerData NewPlayerData0;
    URoJson::JSONStringToUStruct(JsonString0, NewPlayerData0);

}
WangShuXian6 commented 8 months ago

虚幻引擎 Http 请求工具

依赖 UE结构体和JSON字符串互转工具函数类 URoJson

注意:测试时,需要关闭代理软件,否则由于代理和模拟服务都使用了127.0.0.1,虚幻引擎无法触达本地模拟服务端,会导致503响应。

启用 Http模块

Source/Ro2ea/Ro2ea.Build.cs

        PublicDependencyModuleNames.AddRange(new string[]
        {
            "Json", "JsonUtilities", "Http"
        });

服务端模拟

src\auth\users.controller.ts

@Controller('users')
export class UsersController {
  constructor(
    private readonly authService: AuthService,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  @Post('test')
  async createTest(@Body() body) {
    console.log(`测试::${JSON.stringify(body)}`);
    return { dat: body, ResponseMessage: 666 };
  }
}

RoHttpZ 基础请求1

Source/Ro2ea/Public/User/Http/RoHttpZ.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"

#include "RoHttpZ.generated.h"

DECLARE_DELEGATE_TwoParams(FOnHttpRequestCompleted, FHttpResponsePtr /*Response*/, bool /*bWasSuccessful*/);

UCLASS()
class RO2EA_API URoHttpZ : public UObject
{
    GENERATED_BODY()

public:

    static void HttpRequestPost(const FString& Url, const FString& Content, const FOnHttpRequestCompleted& Callback)
    {
        TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
        HttpRequest->OnProcessRequestComplete().BindLambda(
            [Callback](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
            {
                Callback.ExecuteIfBound(Response, bWasSuccessful);
            });

        HttpRequest->SetURL(Url);
        HttpRequest->SetVerb(TEXT("POST"));
        HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
        HttpRequest->SetContentAsString(Content);
        HttpRequest->ProcessRequest();
    }
};

Source/Ro2ea/Private/User/Http/RoHttpZ.cpp


#include "User/Http/RoHttpZ.h"

Headers 请求头工具

Source/Ro2ea/Public/User/Http/Headers.h

#pragma once

#include "CoreMinimal.h"
#include "Interfaces/IHttpRequest.h"
#include "UObject/Object.h"
#include "Headers.generated.h"

USTRUCT(BlueprintType)
struct FHttpHeaders
{
    GENERATED_BODY()

    TMap<FString, FString> HeaderMap;

    // 构造函数,用于初始化默认值
    FHttpHeaders()
    {
        // 设置默认的Content-Type
        SetHeader(TEXT("Content-Type"), TEXT("application/json"));
    }

    // 动态设置或更新头部信息的方法
    void SetHeader(const FString& Key, const FString& Value)
    {
        HeaderMap.Add(Key, Value);
    }

    // 应用所有请求头到HTTP请求的方法
    void ApplyToRequest(TSharedRef<IHttpRequest, ESPMode::ThreadSafe>& HttpRequest) const
    {
        for (const auto& Header : HeaderMap)
        {
            HttpRequest->SetHeader(Header.Key, Header.Value);
        }
    }
};

Source/Ro2ea/Private/User/Http/Headers.cpp


#include "User/Http/Headers.h"

RoHttp 基础请求2

Source/Ro2ea/Public/User/Http/RoHttp.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Headers.h"
#include "RoHttp.generated.h"

UENUM(BlueprintType)
enum class EHttpRequestMethod : uint8
{
    GET UMETA(DisplayName = "GET"),
    POST UMETA(DisplayName = "POST"),
    PUT UMETA(DisplayName = "PUT"),
    DELETE UMETA(DisplayName = "DELETE"),
    PATCH UMETA(DisplayName = "PATCH"),
};

USTRUCT(BlueprintType)
struct FHttpResponseData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 StatusCode;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Content;

    // 根据需要添加更多的响应字段
};

UCLASS()
class RO2EA_API URoHttp : public UObject
{
    GENERATED_BODY()

public:
    using FOnResponseReceived = TDelegate<void(const FHttpResponseData&)>;

    static void HttpRequest(const FString& Url, const EHttpRequestMethod RequestMethod, const FHttpHeaders& Headers,
                            const FString& RequestBody,
                            const FOnResponseReceived& OnResponse)
    {
        TMap<EHttpRequestMethod, FString> RequestMethodMap;
        RequestMethodMap.Add(EHttpRequestMethod::GET, TEXT("GET"));
        RequestMethodMap.Add(EHttpRequestMethod::POST, TEXT("POST"));
        RequestMethodMap.Add(EHttpRequestMethod::PUT, TEXT("PUT"));
        RequestMethodMap.Add(EHttpRequestMethod::DELETE, TEXT("DELETE"));
        RequestMethodMap.Add(EHttpRequestMethod::PATCH, TEXT("PATCH"));

        TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();

        // 设置请求头
        Headers.ApplyToRequest(HttpRequest);

        // 确保映射中包含了请求方法
        if (RequestMethodMap.Contains(RequestMethod))
        {
            HttpRequest->SetVerb(RequestMethodMap[RequestMethod]);
        }
        else
        {
            // 如果未找到相应的请求方法,可以使用默认方法或返回错误
            // 使用GET作为默认方法或添加错误处理
            HttpRequest->SetVerb(TEXT("GET"));
        }

        HttpRequest->SetURL(Url);
        HttpRequest->SetContentAsString(RequestBody);

        // 处理响应
        HttpRequest->OnProcessRequestComplete().BindLambda(
            [OnResponse](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
            {
                FHttpResponseData ResponseData;
                if (bWasSuccessful && Response.IsValid())
                {
                    ResponseData.StatusCode = Response->GetResponseCode();
                    ResponseData.Content = Response->GetContentAsString();
                }
                else
                {
                    // 处理错误情况
                    ResponseData.StatusCode = -1;
                    ResponseData.Content = TEXT("Failed to get a valid response.");
                }

                if (OnResponse.IsBound())
                {
                    OnResponse.Execute(ResponseData);
                }
            });

        // 发送请求
        HttpRequest->ProcessRequest();
    }
};

Source/Ro2ea/Private/User/Http/RoHttp.cpp


#include "User/Http/RoHttp.h"

RoHttpStruct 初步封装 增加自定义结构体参数,响应功能

Source/Ro2ea/Public/User/Http/RoHttpStruct.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "User/Http/RoHttp.h"
#include "User/Util/RoJson.h"
#include "RoHttpStruct.generated.h"

UCLASS()
class RO2EA_API URoHttpStruct : public UObject
{
    GENERATED_BODY()

public:
    using FOnResponseReceivedStruct = TDelegate<void(const FHttpResponseData&)>;

    // 新增方法,处理结构体请求和响应
    template <typename RequestStructType, typename ResponseStructType>
    static void HttpRequestWithStruct(const FString& Url, const EHttpRequestMethod RequestMethod,
                                      const FHttpHeaders& Headers,
                                      const RequestStructType& RequestData,
                                      const TDelegate<void(const ResponseStructType&, bool bWasSuccessful)>& OnResponse)
    {
        FString RequestBody;
        URoJson::UStructToJSONString(RequestData, RequestBody);

        // 创建符合 URoHttp::HttpRequest 需求的委托
        URoHttp::FOnResponseReceived HttpResponseDelegate = URoHttp::FOnResponseReceived::CreateLambda(
            [OnResponse](const FHttpResponseData& HttpResponseData)
            {
                ResponseStructType ResponseData;
                // 确定这里应该是 HttpResponseData 本身提供的成功标志,或你基于状态码自定义的成功逻辑
                bool bSuccess = HttpResponseData.StatusCode >= 200 && HttpResponseData.StatusCode < 300 &&
                    URoJson::JSONStringToUStruct<ResponseStructType>(HttpResponseData.Content, ResponseData);

                // 调用原始委托,这里 bSuccess 变量用于表示是否成功
                OnResponse.ExecuteIfBound(ResponseData, bSuccess);
            });

        URoHttp::HttpRequest(Url, RequestMethod, Headers, RequestBody, HttpResponseDelegate);
    }
};

Source/Ro2ea/Private/User/Http/RoHttpStruct.cpp


#include "User/Http/RoHttpStruct.h"

RoHttpManager 请求管理器,可动态设置请求

Source/Ro2ea/Public/User/Http/RoHttpManager.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Headers.h"
#include "RoHttpManager.generated.h"

UCLASS()
class RO2EA_API URoHttpManager : public UObject
{
    GENERATED_BODY()

private:
    static FString AccessToken;

public:
    // 设置token
    static void SetAccessToken(const FString& Token)
    {
        AccessToken = Token;
    }

    // 获取当前的token
    static FString GetAccessToken()
    {
        return AccessToken;
    }

    // 应用通用请求头
    static void ApplyGlobalHeaders(FHttpHeaders& Headers)
    {
        if (!AccessToken.IsEmpty())
        {
            Headers.SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *AccessToken));
        }
    }
};

Source/Ro2ea/Private/User/Http/RoHttpManager.cpp


#include "User/Http/RoHttpManager.h"

// 按照C++的规则,即使是静态成员,也需要在某个地方定义它,以便为它分配存储空间。
// 初始化静态成员变量
FString URoHttpManager::AccessToken = "";

RoHttpService 高级请求封装 使用更简洁

Source/Ro2ea/Public/User/Http/RoHttpService.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "RoHttpStruct.h"
#include "RoHttp.h"
#include "RoHttpManager.h"
#include "RoHttpService.generated.h"

UCLASS()
class RO2EA_API URoHttpService : public UObject
{
    GENERATED_BODY()

public:
    using FOnResponseReceived = TDelegate<void(const FHttpResponseData&)>;
    using FOnResponseArrayReceived = TDelegate<void(const FHttpResponseData&)>;

    template <typename ResponseStructType, typename ErrorResponseType>
    static FOnResponseReceived CreateResponseDelegate(
        const TFunction<void(const ResponseStructType&, bool)>& SuccessFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc)
    {
        return FOnResponseReceived::CreateLambda(
            [SuccessFunc, ErrorFunc](const FHttpResponseData& HttpResponseData)
            {
                if (HttpResponseData.StatusCode >= 200 && HttpResponseData.StatusCode < 300)
                {
                    ResponseStructType ResponseData;
                    bool bSuccess = URoJson::JSONStringToUStruct<ResponseStructType>(
                        HttpResponseData.Content, ResponseData);

                    UE_LOG(LogTemp, Warning, TEXT("响应 StatusCode: %d"), HttpResponseData.StatusCode);
                    UE_LOG(LogTemp, Warning, TEXT("响应 HttpResponseData.Content: %s"), *HttpResponseData.Content);

                    if (bSuccess || HttpResponseData.StatusCode == 204)
                    {
                        SuccessFunc(ResponseData, true);
                    }
                    else
                    {
                        UE_LOG(LogTemp, Error, TEXT("Failed to parse success response."));
                    }
                }
                else
                {
                    ErrorResponseType ErrorData;
                    bool bErrorParsed = URoJson::JSONStringToUStruct<ErrorResponseType>(
                        HttpResponseData.Content, ErrorData);

                    if (bErrorParsed)
                    {
                        ErrorFunc(ErrorData);
                    }
                    else
                    {
                        UE_LOG(LogTemp, Error, TEXT("Failed to parse error response."));
                    }
                }
            }
        );
    }

    template <typename ResponseStructType, typename ErrorResponseType>
    static FOnResponseArrayReceived CreateArrayResponseDelegate(
        const TFunction<void(const TArray<ResponseStructType>&, bool)>& SuccessFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc)
    {
        return FOnResponseArrayReceived::CreateLambda(
            [SuccessFunc, ErrorFunc](const FHttpResponseData& HttpResponseData)
            {
                if (HttpResponseData.StatusCode >= 200 && HttpResponseData.StatusCode < 300)
                {
                    TArray<ResponseStructType> ResponseData;
                    bool bSuccess = URoJson::JSONStringToUStructArray<ResponseStructType>(
                        HttpResponseData.Content, ResponseData);

                    UE_LOG(LogTemp, Warning, TEXT("响应 StatusCode: %d"), HttpResponseData.StatusCode);
                    UE_LOG(LogTemp, Warning, TEXT("响应 HttpResponseData.Content: %s"), *HttpResponseData.Content);

                    if (bSuccess)
                    {
                        SuccessFunc(ResponseData, bSuccess);
                    }
                    else
                    {
                        UE_LOG(LogTemp, Error, TEXT("Failed to parse success response."));
                    }
                }
                else
                {
                    ErrorResponseType ErrorData;
                    bool bErrorParsed = URoJson::JSONStringToUStruct<ErrorResponseType>(
                        HttpResponseData.Content, ErrorData);

                    if (bErrorParsed)
                    {
                        ErrorFunc(ErrorData);
                    }
                    else
                    {
                        UE_LOG(LogTemp, Error, TEXT("Failed to parse error response."));
                    }
                }
            }
        );
    }

    template <typename RequestStructType, typename ResponseStructType, typename ErrorResponseType>
    static void Post(
        const FString& Url,
        const RequestStructType& RequestData,
        const TFunction<void(const ResponseStructType&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        FString RequestBody;
        URoJson::UStructToJSONString(RequestData, RequestBody);

        // 更新 CreateResponseDelegate 调用,包含错误处理回调
        auto ResponseDelegate = CreateResponseDelegate<ResponseStructType, ErrorResponseType>(ResponseFunc, ErrorFunc);

        URoHttp::HttpRequest(Url, EHttpRequestMethod::POST, Headers, RequestBody, ResponseDelegate);
    }

    template <typename RequestStructType, typename ResponseStructType, typename ErrorResponseType>
    static void PostArray(
        const FString& Url,
        const RequestStructType& RequestData,
        const TFunction<void(const TArray<ResponseStructType>&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        FString RequestBody;
        URoJson::UStructToJSONString(RequestData, RequestBody);

        // 更新 CreateResponseDelegate 调用,包含错误处理回调
        auto ResponseDelegate = CreateArrayResponseDelegate<ResponseStructType, ErrorResponseType>(
            ResponseFunc, ErrorFunc);

        URoHttp::HttpRequest(Url, EHttpRequestMethod::POST, Headers, RequestBody, ResponseDelegate);
    }

    template <typename ResponseStructType, typename ErrorResponseType>
    static void Get(
        const FString& Url,
        const TFunction<void(const ResponseStructType&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        // 更新CreateResponseDelegate调用以包括错误处理回调
        auto ResponseDelegate = CreateResponseDelegate<ResponseStructType, ErrorResponseType>(ResponseFunc, ErrorFunc);

        // 假设GET请求不需要请求体
        URoHttp::HttpRequest(Url, EHttpRequestMethod::GET, Headers, TEXT(""), ResponseDelegate);
    }

    template <typename ResponseStructType, typename ErrorResponseType>
    static void GetArray(
        const FString& Url,
        const TFunction<void(const TArray<ResponseStructType>&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        // 更新CreateResponseDelegate调用以包括错误处理回调
        auto ResponseDelegate = CreateArrayResponseDelegate<ResponseStructType, ErrorResponseType>(
            ResponseFunc, ErrorFunc);

        // 假设GET请求不需要请求体
        URoHttp::HttpRequest(Url, EHttpRequestMethod::GET, Headers, TEXT(""), ResponseDelegate);
    }

    template <typename RequestStructType, typename ResponseStructType, typename ErrorResponseType>
    static void Put(
        const FString& Url,
        const RequestStructType& RequestData,
        const TFunction<void(const ResponseStructType&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        FString RequestBody;
        URoJson::UStructToJSONString(RequestData, RequestBody);

        auto ResponseDelegate = CreateResponseDelegate<ResponseStructType, ErrorResponseType>(ResponseFunc, ErrorFunc);

        URoHttp::HttpRequest(Url, EHttpRequestMethod::PUT, Headers, RequestBody, ResponseDelegate);
    }

    template <typename ResponseStructType, typename ErrorResponseType>
    static void Delete(
        const FString& Url,
        const TFunction<void(const ResponseStructType&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        auto ResponseDelegate = CreateResponseDelegate<ResponseStructType, ErrorResponseType>(ResponseFunc, ErrorFunc);

        URoHttp::HttpRequest(Url, EHttpRequestMethod::DELETE, Headers, TEXT(""), ResponseDelegate);
    }

    template <typename RequestStructType, typename ResponseStructType, typename ErrorResponseType>
    static void Patch(
        const FString& Url,
        const RequestStructType& RequestData,
        const TFunction<void(const ResponseStructType&, bool)>& ResponseFunc,
        const TFunction<void(const ErrorResponseType&)>& ErrorFunc,
        FHttpHeaders Headers = FHttpHeaders()
    )
    {
        URoHttpManager::ApplyGlobalHeaders(Headers);

        FString RequestBody;
        URoJson::UStructToJSONString(RequestData, RequestBody);

        auto ResponseDelegate = CreateResponseDelegate<ResponseStructType, ErrorResponseType>(ResponseFunc, ErrorFunc);

        URoHttp::HttpRequest(Url, EHttpRequestMethod::PATCH, Headers, RequestBody, ResponseDelegate);
    }
};

Source/Ro2ea/Private/User/Http/RoHttpService.cpp


#include "User/Http/RoHttpService.h"

ServerExample ,格式转换,各类请求工具的用例,使用模板

Source/Ro2ea/Public/User/Http/ServerExample.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "RoHttpManager.h"
#include "RoHttpService.h"
#include "ServerExample.generated.h"

//业务请求结构体
USTRUCT(BlueprintType)
struct FMyRequestStruct
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Message;
};
//业务响应结构体
USTRUCT(BlueprintType)
struct FMyResponseStruct
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString ResponseMessage;
};

// 自定义的错误响应
USTRUCT(BlueprintType)
struct FErrorResponseBody
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TArray<FString> message;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString error;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 statusCode;
};

// 自定义结构体 用于转换为JSON字符串
USTRUCT(BlueprintType)
struct FPlayerData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Name;
};

UCLASS()
class RO2EA_API UServerExample : public UObject
{
    GENERATED_BODY()

public:
    // 特定业务逻辑的封装方法
    template <typename RequestStructType, typename ResponseStructType>
    static void PostTestMessageService(
        const RequestStructType& RequestData,
        const TFunction<void(const ResponseStructType&, bool)>& ResponseFunc,
        const FString& Url = TEXT("http://localhost:3000/users/test"), // 默认URL
        FHttpHeaders Headers = FHttpHeaders() // 默认请求头
    )
    {
        // 应用全局headers(比如Authentication token)
        URoHttpManager::ApplyGlobalHeaders(Headers);

        // 直接使用简化后的Post方法
        URoHttpService::Post<RequestStructType, ResponseStructType>(
            Url,
            RequestData,
            ResponseFunc,
            Headers
        );
    }

    // 业务逻辑服务: PostTestMessageService 的用例
    // 使用方式 UServerExample::UsePostTestMessageService();
    // 用例必须使用静态函数
    // 实际项目使用模板,推荐
    static void UsePostTestMessageService();

    // URoHttpService::Post 工具函数用例 高级封装,支持结构体,更易用
    static void UsePost();

    // URoHttpStruct::HttpRequestWithStruct 工具函数用例 原始方法,增加了自定义结构体支持
    static void UseHttpRequestWithStruct();

    // URoHttp::HttpRequest 工具函数用例 原始方法
    static void UseHttpRequest();

    // URoHttpZ::HttpRequestPost 工具函数用例 原始方法
    static void UseHttpRequestZPost();

    // URoJson::UStructToJSONString  URoJson::JSONStringToUStruct 自定义结构体与JSON字符串互转示例
    static void UseRoJson();
};

Source/Ro2ea/Private/User/Http/ServerExample.cpp

#include "User/Http/ServerExample.h"
#include "User/Http/RoHttpStruct.h"
#include "User/Http/RoHttp.h"
#include "User/Http/RoHttpZ.h"

void UServerExample::UsePostTestMessageService()
{
    FMyRequestStruct RequestData;
    RequestData.Message = "UsePostTestMessageService!";

    PostTestMessageService<FMyRequestStruct, FMyResponseStruct, FErrorResponseBody>(
        RequestData,
        [](const FMyResponseStruct& ResponseData, bool bWasSuccessful)
        {
            if (bWasSuccessful)
            {
                UE_LOG(LogTemp, Log, TEXT("Response Message: %s"), *ResponseData.ResponseMessage);
            }
            else
            {
                UE_LOG(LogTemp, Error, TEXT("Request failed."));
            }
        },
        [](const FErrorResponseBody& ErrorResponse)
        {
            UE_LOG(LogTemp, Error, TEXT("Error Message: %s"), *ErrorResponse.message[0]);
        }
    );
}

void UServerExample::UsePost()
{
    FMyRequestStruct RequestData;
    RequestData.Message = "URoHttpService::Post!";

    URoHttpService::Post<FMyRequestStruct, FMyResponseStruct, FErrorResponseBody>(
        TEXT("http://localhost:3000/users/test"),
        RequestData,
        [](const FMyResponseStruct& ResponseData, bool bWasSuccessful)
        {
            UE_LOG(LogTemp, Log, TEXT("Response Message: %s"), *ResponseData.ResponseMessage);
        },
        [](const FErrorResponseBody& ErrorResponse)
        {
            UE_LOG(LogTemp, Error, TEXT("Error Message: %s"), *ErrorResponse.message[0]);
        }
    );
}

void UServerExample::UseHttpRequestWithStruct()
{
    //2 设置请求头和请求数据:
    FHttpHeaders Headers;
    Headers.SetHeader(TEXT("Content-Type"), TEXT("application/json"));
    // 如果需要,设置其他头信息,比如Authorization

    FMyRequestStruct RequestData;
    RequestData.Message = TEXT("URoHttpStruct::HttpRequestWithStruct!");

    //3 发送请求并处理响应:
    URoHttpStruct::HttpRequestWithStruct<FMyRequestStruct, FMyResponseStruct>(
        TEXT("http://localhost:3000/users/test"),
        EHttpRequestMethod::POST,
        Headers,
        RequestData,
        TDelegate<void(const FMyResponseStruct&, bool)>::CreateLambda(
            [](const FMyResponseStruct& ResponseData, bool bWasSuccessful)
            {
                if (bWasSuccessful)
                {
                    // 处理成功的响应
                    UE_LOG(LogTemp, Log, TEXT("Response Message: %s"), *ResponseData.ResponseMessage);
                }
                else
                {
                    // 处理错误
                    UE_LOG(LogTemp, Error, TEXT("HTTP Request Failed"));
                }
            }));
}

void UServerExample::UseHttpRequest()
{
    FHttpHeaders Headers;
    Headers.SetHeader(TEXT("Content-Type"), TEXT("application/json"));
    //Headers.Authorization = TEXT("Bearer your_token_here");

    FString Body = TEXT("{\"key\":\"value - URoHttp::HttpRequest\"}");
    FString URL = TEXT("http://localhost:3000/users/test");

    FString UserToken = "some_dynamic_value";

    URoHttp::HttpRequest(URL, EHttpRequestMethod::POST, Headers, Body, URoHttp::FOnResponseReceived::CreateLambda(
                             [](const FHttpResponseData& ResponseData)
                             {
                                 if (ResponseData.StatusCode >= 200 && ResponseData.StatusCode <= 299)
                                 {
                                     // 处理成功的响应
                                     UE_LOG(LogTemp, Log, TEXT("Response: %s"), *ResponseData.Content);
                                 }
                                 else
                                 {
                                     // 处理错误的响应
                                     UE_LOG(LogTemp, Error, TEXT("HTTP Request Failed: %s"), *ResponseData.Content);
                                 }
                             }));
}

void UServerExample::UseHttpRequestZPost()
{
    FString Content2 = TEXT("{\"key2\":\"value2 - URoHttpZ::HttpRequestPost\"}");
    URoHttpZ::HttpRequestPost(
        TEXT("http://localhost:3000/users/test"), Content2, FOnHttpRequestCompleted::CreateLambda(
            [](FHttpResponsePtr Response, bool bWasSuccessful)
            {
                if (bWasSuccessful && Response.IsValid())
                {
                    // 处理响应内容
                    FString ResponseContent = Response->GetContentAsString();
                    UE_LOG(LogTemp, Log, TEXT("POST Response: %s"), *ResponseContent);
                }
                else
                {
                    // 处理错误
                    UE_LOG(LogTemp, Error, TEXT("POST Request Failed"));
                }
            }));
}

void UServerExample::UseRoJson()
{
    // 结构体转换为json字符串
    FPlayerData PlayerData0;
    PlayerData0.Name = TEXT("PlayerOne000");

    FString JsonString0;
    URoJson::UStructToJSONString(PlayerData0, JsonString0);

    // json字符串转换为结构体
    FPlayerData NewPlayerData0;
    URoJson::JSONStringToUStruct(JsonString0, NewPlayerData0);

    //

    // 结构体转换为json字符串
    FPlayerData PlayerData;
    PlayerData.Name = FString("PlayerOne");

    FString Content;
    FJsonObjectConverter::UStructToJsonObjectString(FPlayerData::StaticStruct(), &PlayerData, Content, 0, 0);

    UE_LOG(LogTemp, Warning, TEXT("结构体转换为json字符串: %s"), *Content);

    // json字符串转换为结构体
    FString JsonString = TEXT("{\"Name\":\"PlayerOne2222\"}");
    FPlayerData PlayerData2;

    if (FJsonObjectConverter::JsonObjectStringToUStruct<FPlayerData>(JsonString, &PlayerData2, 0, 0))
    {
        // 转换成功,PlayerData 现在被填充了 JSON 字符串中的数据
        UE_LOG(LogTemp, Warning, TEXT("json字符串转换为结构体: %s"), *PlayerData2.Name);
    }
    else
    {
        // 处理转换失败的情况
        UE_LOG(LogTemp, Warning, TEXT("json字符串转换为结构体失败"));
    }
}

在其他地方一起调用

注意,仅作示例,BeginPlay() 可能会执行两次。

#include "User/Http/ServerExample.h"

void ARoCharacter::BeginPlay()
{
    Super::BeginPlay();

    //最终示例
    //UServerExample ServerExample;
    //ServerExample.UsePostTestMessageService();
    UServerExample::UsePostTestMessageService();
    UServerExample::UsePost();
    UServerExample::UseHttpRequestWithStruct();
    UServerExample::UseHttpRequest();
    UServerExample::UseHttpRequestZPost();
    UServerExample::UseRoJson();

}