<style>.lazy{display:none}</style> Skip to main content
Tag

FHttpResponsePtr

Unreal C++ Networking – HTTP GET JSON Request Using REST API

By Networking, Tutorial, Unreal No Comments

Hey guys,

Today we are going to start looking at some new content related to networking operations with the Unreal Engine.

Specifically we are going to review how to make HTTP GET calls from within Unreal to an external REST API.

The project files for this video & article can be found on our GitHub page.

In order to start using the HTTP module we first need to modify our Build.cs file to include a few new public dependencies. Specifically Http, Json and JsonUtilities.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Network1 : ModuleRules
{
public Network1(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay",
"Http", "Json", "JsonUtilities",
"UMG" });
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
}
}
public class Network1 : ModuleRules { public Network1(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "Http", "Json", "JsonUtilities", "UMG" }); PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); } }
public class Network1 : ModuleRules
{
  public Network1(ReadOnlyTargetRules Target) : base(Target)
  {
    PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

    PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay",
                    "Http", "Json", "JsonUtilities",
                    "UMG" });

    PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
  }
}

With these dependencies included we can define our Actor that is going to handle the work of doing the network communication.

In this case we are using an actor that will relay it’s information to a custom UUserWidget that is attached to this actor via a WidgetComponent.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
UCLASS()
class NETWORK1_API AHTTPGETActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AHTTPGETActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
/*Assign this function to call when the GET request processes sucessfully*/
void OnGetUsersResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
void OnGetUserByUsernameResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
UFUNCTION()
void SendHTTPGet(FString Username);
private:
void AddUserToWidget(TSharedPtr<FJsonObject> JsonObject);
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "HTTP")
UBoxComponent* OverlapComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "HTTP")
UWidgetComponent* ResponseWidgetComponent;
private:
FHttpModule* Http;
};
UCLASS() class NETWORK1_API AHTTPGETActor : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AHTTPGETActor(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; UFUNCTION() void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); /*Assign this function to call when the GET request processes sucessfully*/ void OnGetUsersResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); void OnGetUserByUsernameResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); UFUNCTION() void SendHTTPGet(FString Username); private: void AddUserToWidget(TSharedPtr<FJsonObject> JsonObject); public: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "HTTP") UBoxComponent* OverlapComponent; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "HTTP") UWidgetComponent* ResponseWidgetComponent; private: FHttpModule* Http; };
UCLASS()
class NETWORK1_API AHTTPGETActor : public AActor
{
  GENERATED_BODY()
  
public:	
  // Sets default values for this actor's properties
  AHTTPGETActor();

protected:
  // Called when the game starts or when spawned
  virtual void BeginPlay() override;

public:	
  // Called every frame
  virtual void Tick(float DeltaTime) override;


  UFUNCTION()
  void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

  /*Assign this function to call when the GET request processes sucessfully*/
  void OnGetUsersResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
  void OnGetUserByUsernameResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);

  UFUNCTION()
  void SendHTTPGet(FString Username);

private:
  void AddUserToWidget(TSharedPtr<FJsonObject> JsonObject);

public:
  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "HTTP")
  UBoxComponent* OverlapComponent;

  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "HTTP")
  UWidgetComponent* ResponseWidgetComponent;

private:
  FHttpModule* Http;
};

The other property is our reference to the FHttpModule that will be used for all of our network communication with the server. This is available to us by including the “Http.h” header in our Actor.

Let’s step through our functions one by one and see how they all communicate.

First thing is our overlap that is triggered when the character interacts with this Actor as well as the instantiation of our components and the FHttpModule.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Sets default values
AHTTPGETActor::AHTTPGETActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
OverlapComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Overlap Area"));
SetRootComponent(OverlapComponent);
ResponseWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("Response Widget"));
ResponseWidgetComponent->SetupAttachment(OverlapComponent);
Http = &FHttpModule::Get();
}
// Called when the game starts or when spawned
void AHTTPGETActor::BeginPlay()
{
Super::BeginPlay();
OverlapComponent->OnComponentBeginOverlap.AddDynamic(this, &ThisClass::OnBeginOverlap);
}
// Sets default values AHTTPGETActor::AHTTPGETActor() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; OverlapComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Overlap Area")); SetRootComponent(OverlapComponent); ResponseWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("Response Widget")); ResponseWidgetComponent->SetupAttachment(OverlapComponent); Http = &FHttpModule::Get(); } // Called when the game starts or when spawned void AHTTPGETActor::BeginPlay() { Super::BeginPlay(); OverlapComponent->OnComponentBeginOverlap.AddDynamic(this, &ThisClass::OnBeginOverlap); }
// Sets default values
AHTTPGETActor::AHTTPGETActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
  PrimaryActorTick.bCanEverTick = true;

  OverlapComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Overlap Area"));
  SetRootComponent(OverlapComponent);

  ResponseWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("Response Widget"));
  ResponseWidgetComponent->SetupAttachment(OverlapComponent);
  
  Http = &FHttpModule::Get();
}

// Called when the game starts or when spawned
void AHTTPGETActor::BeginPlay()
{
  Super::BeginPlay();

  OverlapComponent->OnComponentBeginOverlap.AddDynamic(this, &ThisClass::OnBeginOverlap);
}

With our objects all setup we can now proceed to looking at the OnBeginOverlap method as that is the first thing our character will interact with.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void AHTTPGETActor::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if(ResponseWidgetComponent)
{
UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
if(ResponseWidget)
{
ResponseWidget->ShowLoading(true);
FString Username;
ANetwork1Character* Character = Cast<ANetwork1Character>(OtherActor);
if(Character && !Character->GetUsername().IsEmpty())
{
Username = Character->GetUsername();
}
SendHTTPGet(Username);
}
}
}
void AHTTPGETActor::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { if(ResponseWidgetComponent) { UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget()); if(ResponseWidget) { ResponseWidget->ShowLoading(true); FString Username; ANetwork1Character* Character = Cast<ANetwork1Character>(OtherActor); if(Character && !Character->GetUsername().IsEmpty()) { Username = Character->GetUsername(); } SendHTTPGet(Username); } } }
void AHTTPGETActor::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
                                   UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
  if(ResponseWidgetComponent)
  {
    UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
    if(ResponseWidget)
    {
      ResponseWidget->ShowLoading(true);

      FString Username;
      ANetwork1Character* Character = Cast<ANetwork1Character>(OtherActor);
      if(Character && !Character->GetUsername().IsEmpty())
      {
        Username = Character->GetUsername();
      }

      SendHTTPGet(Username);
    }
  }
}

In the begin overlap we do a few things. We update our user widget ( HTTPResponseWidget ) that a loading operation has started and we try to retrieve the username of the player that interacted with our component.

We then send the players username over to SendHTTPGet() which will try to determine if it’s a valid username or not and make it’s HTTP calls out to an external service.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void AHTTPGETActor::SendHTTPGet(FString Username)
{
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest();
if(Username.IsEmpty())
{
Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnGetUsersResponse);
Request->SetURL("http://localhost:8080/jms-api/users");
}
else
{
Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnGetUserByUsernameResponse);
Request->SetURL(FString::Printf(TEXT("http://localhost:8080/jms-api/user/%s"), *Username));
}
Request->SetVerb("GET");
Request->SetHeader("User-Agent", "X-UnrealEngine-Agent");
Request->SetHeader("Content-Type", "application/json");
Request->ProcessRequest();
}
void AHTTPGETActor::SendHTTPGet(FString Username) { TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest(); if(Username.IsEmpty()) { Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnGetUsersResponse); Request->SetURL("http://localhost:8080/jms-api/users"); } else { Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnGetUserByUsernameResponse); Request->SetURL(FString::Printf(TEXT("http://localhost:8080/jms-api/user/%s"), *Username)); } Request->SetVerb("GET"); Request->SetHeader("User-Agent", "X-UnrealEngine-Agent"); Request->SetHeader("Content-Type", "application/json"); Request->ProcessRequest(); }
void AHTTPGETActor::SendHTTPGet(FString Username)
{
  TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest();

  if(Username.IsEmpty())
  {
    Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnGetUsersResponse);
    Request->SetURL("http://localhost:8080/jms-api/users");
  }
  else
  {
    Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnGetUserByUsernameResponse);
    Request->SetURL(FString::Printf(TEXT("http://localhost:8080/jms-api/user/%s"), *Username));
  }

  Request->SetVerb("GET");
  Request->SetHeader("User-Agent", "X-UnrealEngine-Agent");
  Request->SetHeader("Content-Type", "application/json");
  Request->ProcessRequest();
}

Here is where we start creating our request structure for the HTTP GET call. This means we have to provide a URL and depending on the username we either use it to retrieve a single record or instead get all users available to us.

We also create callbacks via OnProcessRequestComplete to two separate methods: OnGetUsersResponse and OnGetUserByUsernameResponse.

This allows us to handle the response structure per each GET call separately and offload the processing of those requests to different components if required.

We also include some custom headers that can be used for things like the Content-Type definition but also for more complex security and authentication situations.

The OnGetUsersResponse method will be processing this JSON Payload which includes an array of elements as part of the response.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"users": [
{
"id": 1,
"username": "test-user",
"email": "test@email.com"
},
{
"id": 2,
"username": "jms-user",
"email": "admin@jollymonsterstudio.com"
},
{
"id": 3,
"username": "john.smith",
"email": "john@smith.com"
}
]
}
{ "users": [ { "id": 1, "username": "test-user", "email": "test@email.com" }, { "id": 2, "username": "jms-user", "email": "admin@jollymonsterstudio.com" }, { "id": 3, "username": "john.smith", "email": "john@smith.com" } ] }
{
  "users": [
    {
      "id": 1,
      "username": "test-user",
      "email": "test@email.com"
    },
    {
      "id": 2,
      "username": "jms-user",
      "email": "admin@jollymonsterstudio.com"
    },
    {
      "id": 3,
      "username": "john.smith",
      "email": "john@smith.com"
    }
  ]
}

While the OnGetUserByUsernameResponse will process a single user entity response.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"id": 1,
"username": "test-user",
"email": "test@email.com"
}
{ "id": 1, "username": "test-user", "email": "test@email.com" }
{
  "id": 1,
  "username": "test-user",
  "email": "test@email.com"
}

Now let’s take a look at the implementation details for these two payloads.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void AHTTPGETActor::OnGetUsersResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
TSharedPtr<FJsonObject> JsonObject;
if(ResponseWidgetComponent)
{
UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
if(ResponseWidget)
{
ResponseWidget->ShowLoading(false);
if(Response->GetResponseCode() == 200)
{
const FString ResponseBody = Response->GetContentAsString();
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);
if(FJsonSerializer::Deserialize(Reader, JsonObject))
{
TArray<TSharedPtr<FJsonValue>> UserArray = JsonObject->GetArrayField("users");
for(const TSharedPtr<FJsonValue> UserValue : UserArray)
{
AddUserToWidget(UserValue->AsObject());
}
}
}
else
{
// TODO: trigger error
ResponseWidget->ShowError(Response->GetResponseCode(), "Error occured");
}
}
}
}
void AHTTPGETActor::OnGetUserByUsernameResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
TSharedPtr<FJsonObject> JsonObject;
if(ResponseWidgetComponent)
{
UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
if(ResponseWidget)
{
ResponseWidget->ShowLoading(false);
if(Response->GetResponseCode() == 200)
{
const FString ResponseBody = Response->GetContentAsString();
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);
if(FJsonSerializer::Deserialize(Reader, JsonObject))
{
AddUserToWidget(JsonObject);
}
}
else
{
// TODO: trigger error
ResponseWidget->ShowError(Response->GetResponseCode(), "Error occured");
}
}
}
}
void AHTTPGETActor::AddUserToWidget(TSharedPtr<FJsonObject> JsonObject)
{
const int32 UserId = JsonObject->GetIntegerField("id");
const FString Username = JsonObject->GetStringField("username");
UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
if(ResponseWidget)
{
ResponseWidget->AddUser(UserId, Username);
}
}
void AHTTPGETActor::OnGetUsersResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { TSharedPtr<FJsonObject> JsonObject; if(ResponseWidgetComponent) { UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget()); if(ResponseWidget) { ResponseWidget->ShowLoading(false); if(Response->GetResponseCode() == 200) { const FString ResponseBody = Response->GetContentAsString(); TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody); if(FJsonSerializer::Deserialize(Reader, JsonObject)) { TArray<TSharedPtr<FJsonValue>> UserArray = JsonObject->GetArrayField("users"); for(const TSharedPtr<FJsonValue> UserValue : UserArray) { AddUserToWidget(UserValue->AsObject()); } } } else { // TODO: trigger error ResponseWidget->ShowError(Response->GetResponseCode(), "Error occured"); } } } } void AHTTPGETActor::OnGetUserByUsernameResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { TSharedPtr<FJsonObject> JsonObject; if(ResponseWidgetComponent) { UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget()); if(ResponseWidget) { ResponseWidget->ShowLoading(false); if(Response->GetResponseCode() == 200) { const FString ResponseBody = Response->GetContentAsString(); TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody); if(FJsonSerializer::Deserialize(Reader, JsonObject)) { AddUserToWidget(JsonObject); } } else { // TODO: trigger error ResponseWidget->ShowError(Response->GetResponseCode(), "Error occured"); } } } } void AHTTPGETActor::AddUserToWidget(TSharedPtr<FJsonObject> JsonObject) { const int32 UserId = JsonObject->GetIntegerField("id"); const FString Username = JsonObject->GetStringField("username"); UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget()); if(ResponseWidget) { ResponseWidget->AddUser(UserId, Username); } }
void AHTTPGETActor::OnGetUsersResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
  TSharedPtr<FJsonObject> JsonObject;

  if(ResponseWidgetComponent)
  {
    UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
    if(ResponseWidget)
    {
      ResponseWidget->ShowLoading(false);

      if(Response->GetResponseCode() == 200)
      {
        const FString ResponseBody = Response->GetContentAsString();

        TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);

        if(FJsonSerializer::Deserialize(Reader, JsonObject))
        {
          TArray<TSharedPtr<FJsonValue>> UserArray = JsonObject->GetArrayField("users");

          for(const TSharedPtr<FJsonValue> UserValue : UserArray)
          {
            AddUserToWidget(UserValue->AsObject());
          }
        }
      }
      else
      {
        // TODO: trigger error
        ResponseWidget->ShowError(Response->GetResponseCode(), "Error occured");
      }
    }
  }
}


void AHTTPGETActor::OnGetUserByUsernameResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
  TSharedPtr<FJsonObject> JsonObject;

  if(ResponseWidgetComponent)
  {
    UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
    if(ResponseWidget)
    {
      ResponseWidget->ShowLoading(false);

      if(Response->GetResponseCode() == 200)
      {
        const FString ResponseBody = Response->GetContentAsString();

        TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);

        if(FJsonSerializer::Deserialize(Reader, JsonObject))
        {
          AddUserToWidget(JsonObject);
        }
      }
      else
      {
        // TODO: trigger error
        ResponseWidget->ShowError(Response->GetResponseCode(), "Error occured");
      }
    }
  }
}

void AHTTPGETActor::AddUserToWidget(TSharedPtr<FJsonObject> JsonObject)
{
  const int32 UserId = JsonObject->GetIntegerField("id");
  const FString Username = JsonObject->GetStringField("username");

  UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
  if(ResponseWidget)
  {
    ResponseWidget->AddUser(UserId, Username);
  }
}

Lastly in our OnGetUsersResponse and OnGetUserByUsernameResponse methods we can use the reference to the FHttpRequestPtr and FHttpResponsePtr parameters to determine if our calls were successful and came back with a status code of 200

Additionally we can observe how the JSON structure is parsed by first using the FJsonObject in combination with the TJsonReader

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const FString ResponseBody = Response->GetContentAsString();
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);
const FString ResponseBody = Response->GetContentAsString(); TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);
const FString ResponseBody = Response->GetContentAsString(); 
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);

This gives us the ability to then get at the individual properties of a JSON response. Specifically since one requires access to an Array.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
if(FJsonSerializer::Deserialize(Reader, JsonObject))
{
TArray<TSharedPtr<FJsonValue>> UserArray = JsonObject->GetArrayField("users");
for(const TSharedPtr<FJsonValue> UserValue : UserArray)
{
AddUserToWidget(UserValue->AsObject());
}
}<br><br>
if(FJsonSerializer::Deserialize(Reader, JsonObject)) { TArray<TSharedPtr<FJsonValue>> UserArray = JsonObject->GetArrayField("users"); for(const TSharedPtr<FJsonValue> UserValue : UserArray) { AddUserToWidget(UserValue->AsObject()); } }<br><br>
if(FJsonSerializer::Deserialize(Reader, JsonObject))
{
  TArray<TSharedPtr<FJsonValue>> UserArray = JsonObject->GetArrayField("users");

  for(const TSharedPtr<FJsonValue> UserValue : UserArray)
  {
    AddUserToWidget(UserValue->AsObject());
  }
}

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void AHTTPGETActor::AddUserToWidget(TSharedPtr<FJsonObject> JsonObject)
{
const int32 UserId = JsonObject->GetIntegerField("id");
const FString Username = JsonObject->GetStringField("username");
UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
if(ResponseWidget)
{
ResponseWidget->AddUser(UserId, Username);
}
}
void AHTTPGETActor::AddUserToWidget(TSharedPtr<FJsonObject> JsonObject) { const int32 UserId = JsonObject->GetIntegerField("id"); const FString Username = JsonObject->GetStringField("username"); UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget()); if(ResponseWidget) { ResponseWidget->AddUser(UserId, Username); } }
void AHTTPGETActor::AddUserToWidget(TSharedPtr<FJsonObject> JsonObject)
{
  const int32 UserId = JsonObject->GetIntegerField("id");
  const FString Username = JsonObject->GetStringField("username");

  UHTTPResponseWidget* ResponseWidget = Cast<UHTTPResponseWidget>(ResponseWidgetComponent->GetWidget());
  if(ResponseWidget)
  {
    ResponseWidget->AddUser(UserId, Username);
  }
}

By using the various getters ( GetArrayField / GetIntegerField / GetStringField ) we can iterate and access the various properties of our payload.

That’s it, you successfully processed a JSON response from an external service using your Unreal code.

Hope this helps you guys make your own network requests.

If you would like to see more examples check out the video as well as the GitHub project for examples of child classes that move the player and particles around.