UE5で簡単なGraphQLのやり取りを行いたい
マーケットプレイスで検索すると、GraphQLを扱えるようになるコンテンツが見つかります
ただ、そこそこいい値段がするので、サーバーとの簡単な(認証など不要な)やり取りができるコードを書いてみました
環境
ソフトウェア | バージョン |
---|---|
MacOS | 10.15.7 |
UnrealEngine | 5.0.0 |
Visual Studio Code | 1.61.2 |
Xcode | 12.4 |
.NET SDK | 5.0.402 |
基本設計
UObjectを継承して、Blueprint上でクエリー文の入力と、イベントによる結果の受信を行います
プロジェクト名はGraphqlTestとして進めます
コード
Build.cs
ファイルのPublicDependencyModuleNames
にHttp
,Json
,JsonUtilities
を追加します
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Http", "Json", "JsonUtilities" });
// Fill out your copyright notice in the Description page of Project Settings.
# pragma once
# include "Http.h" // Http関連が使用できるように追加
# include "CoreMinimal.h"
# include "UObject/NoExportTypes.h"
# include "GraphqlObject.generated.h"
/**
*
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBlueprintAssignableSignature);
UCLASS(Blueprintable, BlueprintType)
class GRAPHQLTEST_API UGraphqlObject : public UObject
{
GENERATED_BODY()
public:
UGraphqlObject();
FHttpModule *Http;
private:
public:
// GraphQLのサーバをBlueprintから指定できるようにプロパティを公開
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Graphql")
FString url;
// 受信結果をBlueprintから参照できるように読み込み専用のプロパティを追加
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Graphql")
FString ContentAsString;
// 自身のインスタンスを作成するメソッド
UFUNCTION(BlueprintPure, Category = "Graphql", meta = (DisplayName = "Create Object", Keywords = "Create Graphql Object", ShortTooltip = "Creates an empty Graphql Object"))
static UGraphqlObject *Create();
// HTTP通信を行ってレスポンスが返ってきた際のイベント処理
void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
// 受診後のイベントを発行するためのメソッド
UPROPERTY(BlueprintAssignable, Category = "Dynamic Delegate Component")
FBlueprintAssignableSignature OnResponseReceivedDispatcherCalled;
// クエリーを発行するためのメソッド
UFUNCTION(BlueprintCallable, Category = "Graphql")
void Query(FString document);
};
// Fill out your copyright notice in the Description page of Project Settings.
# include "GraphqlObject.h"
UGraphqlObject::UGraphqlObject()
: Super()
{
Http = &FHttpModule::Get();
}
UGraphqlObject* UGraphqlObject::Create() {
return NewObject<UGraphqlObject>();
}
void UGraphqlObject::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!Response.IsValid())
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, "No Valid");
}
else if (EHttpResponseCodes::IsOk(Response->GetResponseCode()))
{
ContentAsString = Response->GetContentAsString();
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<> > Reader = TJsonReaderFactory<>::Create(ContentAsString);
// Jsonオブジェクトをデシリアライズ
if (FJsonSerializer::Deserialize(Reader, JsonObject))
{
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, "Received JSON String");
//ここに、JSON StringをパースしてJSON Objectとして保持する工程を追加する
OnResponseReceivedDispatcherCalled.Broadcast();
}
}
}
void UGraphqlObject::Query(FString document)
{
// Jsonデータの作成
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
JsonObject->SetStringField("query", document);
// JsonStringにJson書き出し
FString JsonString;
TSharedRef<TJsonWriter<TCHAR> > JsonWriter = TJsonWriterFactory<>::Create(&JsonString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter);
// Httpリクエストの作成
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest();
Request->OnProcessRequestComplete().BindUObject(this, &UGraphqlObject::OnResponseReceived);
Request->SetURL(url);
Request->SetVerb("POST");
Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->SetHeader(TEXT("Accepts"), TEXT("application/json"));
Request->SetContentAsString(JsonString);
Request->ProcessRequest();
}
Blueprint上での組み立て
図の上段が、BeginPlayから始まるGraphqlObjectの準備とイベント処理
図の下段は、クエリーを発行する処理
Queryの後にDelayなどがつながっているのはテスト用につなげたもので、不要です
変数に自作したクラスをセットして、Blueprint上にドラッグ&ドロップで配置
そこからノードをつなげていくことで、クエリーを実行できるが、createメソッドで作ったオブジェクトを、Blueprintの変数に再代入しないと、オブジェクトの要素にアクセスできません
UActorを継承すれば、こんなヘンテコなつなげ方をする必要はないのだけれど、Actorとしての機能は必要ないと考えて、UObjectを継承しています
追記(2021/10/29)
JSONはそのままではBlueprintに出力できない
何かしらでラップしないとBlueprintでは扱えないようですが、ここでは触れません
今回は文字として出力しています
以下のソースが参考になると思います
さいごに
マーケットプレイスにあるGraphQLが扱えるようになるものは、受信後の処理をイベントではなく、Latentノードというもので実装している
こちらの方がBlueprint上での取り扱いが直感的にできそう