Studio GuideWorld SDK Guide
Log In

HTTP request from the Multiplay server

You can make HTTP requests from the ZEPETO World Multiplay server using the ZEPETO.Multiplay.HttpService module. Integrate external web services into your business logic operations, data storage, statistical analysis, error tracking, and more.


❗️

Caution

  • Please ensure that only the HTTPS protocol is used, while HTTP is supported only in the development environment.
  • Requests are allowed only on ports 80 and 443.
  • The maximum size for request and response bodies is limited to 16KB.
  • Keep the number of requests per minute below 500 to avoid potential limitations on the world service if excessive requests occur.
  • Requests will fail if external web services do not respond within 5 seconds.
  • Ensure that the content-type in response headers matches the values defined in the HttpContentType enum; otherwise, requests will fail.
  • Given the possibility of web requests failing for various reasons, it is recommended to code defensively.

ZEPETO.Multiplay.HttpService

📘

Please refer to the following guide. [ZEPETO.Multiplay.HttpService API]


Methods

MethodDescription
HttpService.getAsync(url: string, headers?: HttpHeader): PromisePerform HTTP GET requests asynchronously.

[Parameters]
- url : The web address to send the request to.
- headers : HTTP request headers. (Optional)

[Return Value]
- Promise<HttpResponse> : Returns an HttpResponse object containing information about the response as a Promise.
HttpService.postAsync(url: string, body: HttpBodyType, headers?: HttpHeader): PromisePerform HTTP POST requests asynchronously.

[Parameters]
- url : The web address to send the request to.
- body : The request body content.
- headers : HTTP request headers. (Optional)

[Return Value]
- Promise<HttpResponse> : Returns an HttpResponse object containing information about the response as a Promise.
HttpService.postAsync(url: string, body: HttpBodyType, httpContentType: HttpContentType, headers?: HttpHeader): PromisePerform HTTP POST requests asynchronously.

[Parameters]
- url : The web address to send the request to.
- body : The request body content.
- httpContentType : Specifies the request Content-Type header.
- headers : HTTP request headers. (Optional)

[Return Value]
- Promise<HttpResponse> : Returns an HttpResponse object containing information about the response as a Promise.

When using this signature, if you add 'Content-Type' to headers, it will be overwritten by what is specified in httpContentType.

Other Declarations

DeclarationDescription
HttpContentTypeEnumeration of constants that specify the Content-Type for HTTP headers.

- ApplicationJson : 'application/json'
- ApplicationXml : 'application/xml'
- ApplicationUrlEncoded : 'application/x-www-form-urlencoded'
- TextPlain : 'text/plain'
- TextXml : 'text/xml'
HttpBodyTypeType for the HTTP request body content, which can be either a string or an object with string keys and any values.
HttpHeaderType for defining HTTP request headers, where the property values can be either strings or numbers in the object.
HttpResponseInterface that includes information about the HTTP request result and response data.

- statusCodeHTTP : A number representing the status code of the response. Typically, 200 indicates a successful request.
- statusTextHTTP : A string representing the status message of the response. Typically, "OK" indicates a successful request.
- response : A string containing the HTTP response body data.

📘

HTTP Status

https://developer.mozilla.org/en-US/docs/Web/HTTP/Status


Code Samples

Basic GET Request

Let's create a simple GET request example using HttpService.getAsync. When a new client connects to the Multiplay Room, we will send an HTTP request to an external web service and log the result.

restcountries.com is an open API that provides information about various countries. We will use this service to find out the capital city of Japan.

Set up Multiplay, then open World.multiplay/index.ts and write the server script as follows.

📘

Please refer to the following guide. [Multiplay]


import { Sandbox, SandboxOptions, SandboxPlayer } from "ZEPETO.Multiplay";
import { HttpService } from "ZEPETO.Multiplay.HttpService";

export default class extends Sandbox {

    onCreate(options: SandboxOptions) { }
    onJoin(client: SandboxPlayer) { this.findCapitalCity() }
    onLeave(client: SandboxPlayer, consented?: boolean) { }

    findCapitalCity() {
        // Make the request to the restcountries API
        HttpService.getAsync("https://restcountries.com/v3.1/name/japan?fields=capital")
            // Handler for the HTTP Response   
            .then((httpResponse) => {
                // Parse the JSON response data from the API (which is nested in HTTP Response)
                let countries = JSON.parse(httpResponse.response);
                console.log("The capital city of Japan is:");
                console.log(`${countries[0].capital}`);
            });
    }
}

Code description:

  • When a client connects to the Multiplay Room, the onJoin function is triggered, and it calls findCapitalCity.
  • If the getAsync call to the restcountries API is successful, you can access the httpResponse object in the then callback.
  • Parse httpResponse.response to convert the API response, which is in JSON format, into an object.
  • By referring to the structure of the API response, you access the desired properties and then print their values.

When you run the Multiplay server in the Unity Editor and play the scene, the following will be displayed in the Console.

The capital city of Japan is: 
Tokyo

Defensive Coding Practices

  • Meanwhile, as mentioned in the introductory precautions, web requests can fail for various reasons, including changes in web addresses or API response formats.
  • Failure to properly handle these requests can have various adverse effects on World play, so it is recommended to code defensively, especially when dealing with HTTP requests.
  • The following is an example of applying several defensive coding techniques to the code above.
   findCapitalCity() {
    // Make the request to the restcountries API
    HttpService.getAsync(
        "https://restcountries.com/v3.1/name/japan?fields=capital",
        { 'Accept': HttpContentType.ApplicationJson })
        // Handler for the HTTP Response
        .then((httpResponse) => {
            // Check if the API call returned 200(OK) or not
            if (httpResponse.statusCode != 200) {
                // If it was not 200(OK), do not proceed and raise a custom error
                throw (`API Error: ${httpResponse.statusCode} ${httpResponse.statusText}`);
            }
            // Return the JSON response data from the API
            return httpResponse.response;
        })
        // Handler for the JSON response data from the API
        .then((response) => {
            // Parse the JSON response data
            let countries = JSON.parse(response);
            // Check if the API response data is valid
            if (!Array.isArray(countries) || !countries.length) {
                // If it is not an array, or is empty
                // do not proceed and raise a custom error
                throw (`API Error: Response data is not valid`);
            }
            let country = countries[0];
            // Check if the 'capital' field is valid
            if (!country.capital) {
                // If the 'capital' field does not exist, or is empty
                // do not proceed and raise a custom error
                throw (`API Error: 'capital' field not valid`);
            }
            console.log("The capital city of Japan is:")
            console.log(`${country.capital}`);
        })
        // Handler for errors occurring in the getAsync call and 'then' clauses.
        .catch((reason) => {
            console.log("API Request error");
            console.log(reason);
        });
}

The defensive coding techniques applied here include the following:

  • Using the Accept Header: The Accept header is used to specify the expected Content-Type for the response body. Depending on the server, the response Content-Type can be adjusted based on the Accept header.
  • Checking HttpResponse.statusCode: The HttpResponse.statusCode property is used to verify the success of the request.
  • Validating JSON Data Structure: You confirm whether the data structure of the object parsed with JSON.parse matches the expected structure.
  • Verifying Property Existence: You ensure that the properties you intend to use actually exist in the object.
  • Utilizing the Promise's catch Method: The catch method of the Promise is used to handle errors that may occur during API requests and response processing.

These techniques protect the code to operate reliably in the face of unexpected errors, enhancing its robustness.


Integrating with Clients via Room Messages

HTTP requests are only possible from the Multiplay server.

However, by using Multiplay Room Messages, you can trigger the server to send HTTP requests to external web services from the client and utilize the responses within the client.

The following example demonstrates client-server integration. In this demonstration, when a button on the client's UI is pressed, the country's capital corresponding to the button is displayed.

📘

Please refer to the following guide. [Multiplay Room Message]



Client Code

import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import { Room } from 'ZEPETO.Multiplay'
import { ZepetoWorldMultiplay } from 'ZEPETO.World';
import { Button, Text } from 'UnityEngine.UI'

export default class SampleScript extends ZepetoScriptBehaviour {

    public multiplay: ZepetoWorldMultiplay;
    public room: Room;
    public countryButtons: Button[];
    public capitalText: Text;


    Start() {
        interface Response {
            capitals: string[]
        }

        for (const countryButton of this.countryButtons) {
            countryButton.onClick.AddListener(() => {
                if (this.room !== null) {
                    // Send the country name as a 'client-to-server' type message to the server
                    this.room.Send("client-to-server", countryButton.GetComponentInChildren<Text>().text);
                }
            });
        };

        this.multiplay.RoomCreated += (room: Room) => {
            this.room = room;
            // Handle messages received with a 'server-to-client' type
            this.room.AddMessageHandler("server-to-client", (message: Response) => {
                // For countries with multiple capital cities,
                // join the string elements with ', ' and display on the scene
                this.capitalText.text = message.capitals.join(", ");
            });
        };
    }
}

Code description:

  • We define a listener that iterates through buttons displaying the names of countries. When clicked, this listener sends a Room Message to the Multiplay server.
  • This listener sends the country's name displayed on the button as a 'client-to-server' type message.
  • To handle responses, we also define a listener for processing Room Messages received as 'server-to-client' type.
  • This listener displays the capital city name received from the server on the screen. If there are multiple capital cities, they are displayed on the screen, separated by commas.

Server Code

import { Sandbox, SandboxOptions, SandboxPlayer } from "ZEPETO.Multiplay";
import { HttpService } from "ZEPETO.Multiplay.HttpService";

export default class extends Sandbox {

    onCreate(options: SandboxOptions) {
        // Handle messages received with a 'client-to-server' type
        this.onMessage("client-to-server", (client, message) => {
            console.log(`Client ${client.userId} sent request. Message: ${message}`);
            this.findCapitalCity(message, client);
        });
    }

    onJoin(client: SandboxPlayer) { }
    onLeave(client: SandboxPlayer, consented?: boolean) { }

    findCapitalCity(countryName: string, client: SandboxPlayer) {
        // Request API with the countryName as a path parameter
        // The countryName is sent by the client as a room message
        HttpService.getAsync(`https://restcountries.com/v3.1/name/${countryName}?fields=capital`)
            .then((httpResponse) => {
                if (httpResponse.statusCode != 200) {
                    throw (`API Error: ${httpResponse.statusCode} ${httpResponse.statusText}`);
                }
                return httpResponse.response;
            })
            .then((response) => {
                let countries = JSON.parse(response);
                if (!Array.isArray(countries) || !countries.length) {
                    throw (`API Error: Response data is not valid`);
                }
                let country = countries[0];
                if (!country.capital) {
                    throw (`API Error: 'capital' field not valid`);
                }
                // Send a 'server-to-client' type message to the client
                // The message is an object containing capital city names
                client.send("server-to-client", {
                    "capitals": country.capital
                });
            })
            .catch((reason) => {
                console.log("API Request error");
                console.log(reason);
            });
    }
}

Code description:

  • We define a listener that, upon receiving a 'client-to-server' type Multiplay Room Message, calls findCapitalCity.
  • We construct an address for the Multiplay Room Message using the country name and make a getAsync call.
  • If the getAsync call is successful, the response is handled as in previous examples.
  • The capital city name obtained from the response of the restcountries API is sent to the client as a 'server-to-client' type Room Message.

POST Request

Lastly, let's create a POST request example using HttpService.postAsync.

postman-echo is a service that provides structured responses showing what content was received from web requests, making it effective for verifying if the client has configured the request correctly.

Through this example, we will set up a POST request with query parameters, request body, and headers and ensure that the request is properly configured.

Set up Multiplay, then open World.multiplay/index.ts and write the server script as follows.

import { Sandbox, SandboxOptions, SandboxPlayer } from "ZEPETO.Multiplay";
import { HttpContentType, HttpService } from "ZEPETO.Multiplay.HttpService";

export default class extends Sandbox {

    onCreate(options: SandboxOptions) { }
    onJoin(client: SandboxPlayer) { this.echoPost() }
    onLeave(client: SandboxPlayer, consented?: boolean) { }

    echoPost() {
        HttpService.postAsync(
            // API Endpoint with a query parameter
            "https://postman-echo.com/post?argKey=argValue",
            // JSON Request Body
            { "dataKey": "dataValue" },
            // Request Content Type
            HttpContentType.ApplicationJson,
            // HTTP Header
            { "header-key": "header-value" })
            .then((httpResponse) => {
                if (httpResponse.statusCode != 200) {
                    throw (`API Error: ${httpResponse.statusCode} ${httpResponse.statusText}`);
                }
                return httpResponse.response;
            })
            .then((response) => {
                let parsed = JSON.parse(response);
                console.log(`Query parameter: argKey:${parsed.args.argKey}`);
                console.log(`Request body: dataKey:${parsed.data.dataKey}`);
                console.log(`Request header: header-key:${parsed.headers["header-key"]}`)
            })
            .catch((reason) => {
                console.log("API Request error");
                console.log(reason);
            });
    }
}

Code description:

  • When a client connects to the Multiplay Room, the echoPost function is called from the onJoin trigger.
  • When making the postAsync request:
    • In the first parameter, we construct the URL string with query parameters.
    • In the second parameter, we set the request body content.
    • In the fourth parameter, we configure the request header.
    • To specify that the request body is in JSON format, we set the 'application/json' Content-Type in the third parameter.
  • If the postAsync call is successful, we can access the httpResponse object in the then callback.
  • We parse httpResponse.response to convert the API response in JSON format into an object.
  • Referring to the structure of the API response, we use Console output to verify whether our HTTP request's query parameters, request body, and request header are correctly configured.

When you run the Multiplay server in the Unity Editor and play the scene, the following will be displayed in the Console.

Query parameter: argKey:argValue
Request body: dataKey:dataValue
Request header: header-key:header-value