---
title: 来自Multiplay服务器的HTTP请求
slug: world-sdk-guide-ch/multiplayhttp
docTags: 
createdAt: 2024-09-02T08:34:13.135Z
---

您可以使用 ZEPETO.Multiplay.HttpService 模块从 ZEPETO World Multiplay 服务器发出 HTTP 请求。将外部网络服务集成到您的业务逻辑操作、数据存储、统计分析、错误跟踪等中。



:::hint{type="danger"}
- 请确保仅使用 HTTPS 协议，而 HTTP 仅在开发环境中支持。
- 仅允许在 80 和 443 端口上发出请求。
- 请求和响应主体的最大大小限制为 16KB。
- 每分钟的请求数量应保持在 500 以下，以避免在发生过多请求时对世界服务施加潜在限制。
- 如果外部网络服务在 5 秒内未响应，请求将失败。
- 确保响应头中的内容类型与 `HttpContentType` 枚举中定义的值匹配；否则，请求将失败。
- 考虑到网络请求可能因各种原因失败，建议采取防御性编码。
:::



## ZEPETO.Multiplay.HttpService

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南。 \[[ZEPETO.Multiplay.HttpService API](https://developer.zepeto.me/docs/multiplay-server/interfaces/ZEPETO_Multiplay_HttpService.HttpService)]
:::



### 方法

| **方法**                                                                                                                  | **描述**                                                                                                                                                                                                                                                                                                                                        |
| ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| HttpService.getAsync(url: string, headers?: HttpHeader): Promise                                                        | 异步执行 HTTP GET 请求。<br />**\[参数]**<br />- url : 发送请求的网页地址。<br />- headers : HTTP 请求头。 (可选)<br /><br />**\[返回值]**<br />- Promise\<HttpResponse> : 返回一个 HttpResponse 对象，包含有关响应的信息作为 Promise                                                                                                                                                       |
| HttpService.postAsync(url: string, body: HttpBodyType, headers?: HttpHeader): Promise                                   | 异步执行 HTTP POST 请求。<br />**\[参数]**<br />- url : 发送请求的网页地址。<br />- body : 请求体内容。<br />- headers : HTTP 请求头。 (可选)<br /><br />**\[返回值]**<br />- Promise\<HttpResponse> : 返回一个 HttpResponse 对象，包含有关响应的信息作为 Promise                                                                                                                                 |
| HttpService.postAsync(url: string, body: HttpBodyType, httpContentType: HttpContentType, headers?: HttpHeader): Promise | 异步执行 HTTP POST 请求。<br />**\[参数]**<br />- url : 发送请求的网页地址。<br />- body : 请求体内容。<br />- httpContentType : 指定请求的 Content-Type 头。<br />- headers : HTTP 请求头。 (可选)<br /><br />**\[返回值]**<br />- Promise\<HttpResponse> : 返回一个 HttpResponse 对象，包含有关响应的信息作为 Promise。<br /><br />使用此签名时，如果您在 headers 中添加 'Content-Type'，它将被 httpContentType 中指定的内容覆盖。 |

### 其他声明

| **声明**          | **描述**                                                                                                                                                                                                                                                |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| HttpContentType | 指定HTTP头的Content-Type的常量枚举。<br /><br />- ApplicationJson : 'application/json'<br />- ApplicationXml : 'application/xml'<br />- ApplicationUrlEncoded : 'application/x-www-form-urlencoded'<br />- TextPlain : 'text/plain'<br />- TextXml : 'text/xml' |
| HttpBodyType    | HTTP请求体内容的类型，可以是字符串或具有字符串键和任意值的对象。                                                                                                                                                                                                                    |
| HttpHeader      | 用于定义HTTP请求头的类型，其中属性值可以是字符串或对象中的数字。                                                                                                                                                                                                                    |
| HttpResponse    | 包含有关HTTP请求结果和响应数据的信息的接口。<br /><br />- statusCodeHTTP : 表示响应状态码的数字。通常，200表示请求成功。<br />- statusTextHTTP : 表示响应状态消息的字符串。通常，"OK"表示请求成功。<br />- response : 包含HTTP响应体数据的字符串。                                                                                |

:::hint{type="info"}
**📘 HTTP状态**
[https://developer.mozilla.org/en-US/docs/Web/HTTP/Status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
:::



## 代码示例

### 基本的 GET 请求

让我们使用 `HttpService.getAsync`. 当一个新客户端连接到 Multiplay 房间时，我们将向外部网络服务发送一个 HTTP 请求并记录结果。

[restcountries.com](https://restcountries.com/) 是一个开放的API，提供有关各国的信息。我们将使用此服务来查找日本的首都。

设置Multiplay，然后打开World.multiplay/index.ts并按如下方式编写服务器脚本。

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南。 \[[多人游戏](docId\:VrCnxwq2U9DbMAmYI6-9D)]
:::



```typescript
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("日本的首都城市是:");
                console.log(`${countries[0].capital}`);
            });
    }
}
```

代码描述:

- 当客户端连接到Multiplay房间时，`onJoin`函数被触发，并调用`findCapitalCity`。
- 如果对restcountries API的`getAsync`调用成功，您可以在`httpResponse`对象中访问`then`回调。
- 解析`httpResponse.response`以将API响应（以JSON格式）转换为对象。
- 通过参考API响应的结构，您可以访问所需的属性，然后打印它们的值。

当您在Unity编辑器中运行Multiplay服务器并播放场景时，控制台将显示以下内容。

```text
日本的首都城市是：
东京
```

### 防御性编码实践

- 同时，如引言中的预防措施所提到的，网络请求可能因各种原因而失败，包括网络地址或API响应格式的变化。
- 未能正确处理这些请求可能对世界游戏产生各种不利影响，因此建议进行防御性编码，特别是在处理HTTP请求时。
- 以下是将几种防御性编码技术应用于上述代码的示例。

```typescript
   findCapitalCity() {
    // 向restcountries API发出请求
    HttpService.getAsync(
        "https://restcountries.com/v3.1/name/japan?fields=capital",
        { 'Accept': HttpContentType.ApplicationJson })
        // HTTP响应的处理程序
        .then((httpResponse) => {
            // 检查API调用是否返回200(OK)
            if (httpResponse.statusCode != 200) {
                // 如果不是200(OK)，则不继续并引发自定义错误
                throw (`API错误: ${httpResponse.statusCode} ${httpResponse.statusText}`);
            }
            // 返回API的JSON响应数据
            return httpResponse.response;
        })
        // API的JSON响应数据的处理程序
        .then((response) => {
            // 解析JSON响应数据
            let countries = JSON.parse(response);
            // 检查API响应数据是否有效
            if (!Array.isArray(countries) || !countries.length) {
                // 如果不是数组，或为空
                // 不继续并引发自定义错误
                throw (`API错误: 响应数据无效`);
            }
            let country = countries[0];
            // 检查'capital'字段是否有效
            if (!country.capital) {
                // 如果'capital'字段不存在，或为空
                // 不继续并引发自定义错误
                throw (`API错误: 'capital'字段无效`);
            }
            console.log("日本的首都城市是:")
            console.log(`${country.capital}`);
        })
        // 处理在getAsync调用和'then'子句中发生的错误。
        .catch((reason) => {
            console.log("API请求错误");
            console.log(reason);
        });
}
```

这里应用的防御性编码技术包括以下内容：

- 使用 `Accept` 头部：`Accept` 头部用于指定响应体的预期 Content-Type。根据服务器的不同，响应的 Content-Type 可以根据 `Accept` 头部进行调整。
- 检查 `HttpResponse.statusCode`: `HttpResponse.statusCode` 属性用于验证请求的成功。
- 验证 JSON 数据结构：您确认使用 `JSON.parse` 解析的对象的数据结构是否与预期结构匹配。
- 验证属性存在性：您确保您打算使用的属性在对象中确实存在。
- 利用 Promise 的 `catch` 方法：Promise 的 `catch` 方法用于处理在 API 请求和响应处理过程中可能发生的错误。

这些技术保护代码在面对意外错误时可靠运行，增强了其稳健性。



### 通过房间消息与客户端集成

HTTP 请求仅能从 Multiplay 服务器发出。

然而，通过使用 Multiplay 房间消息，您可以触发服务器从客户端向外部网络服务发送 HTTP 请求，并在客户端中利用响应。

以下示例演示了客户端与服务器的集成。在此演示中，当客户端 UI 上的按钮被按下时，显示与该按钮对应的国家首都。

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南。 \[[Multiplay Room Message](docId\:WwZa8FxWMtcm7xkfePebd) ]
:::



![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-tfcubL3eArDSHrPB-D9AO-20240904-102411.gif)



**客户端代码**

```typescript
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) {
                    // 发送国家名称作为 '客户端到服务器' 类型消息到服务器
                    this.room.Send("client-to-server", countryButton.GetComponentInChildren<Text>().text);
                }
            });
        };

        this.multiplay.RoomCreated += (room: Room) => {
            this.room = room;
            // 处理以 '服务器到客户端' 类型接收的消息
            this.room.AddMessageHandler("server-to-client", (message: Response) => {
                // 对于拥有多个首都城市的国家，
                // 将字符串元素用 ', ' 连接并在场景中显示
                this.capitalText.text = message.capitals.join(", ");
            });
        };
    }
}
```

代码描述：

- 我们定义了一个监听器，遍历显示国家名称的按钮。当点击时，这个监听器向Multiplay服务器发送一个房间消息。
- 这个监听器将按钮上显示的国家名称作为 '客户端到服务器' 类型消息发送。
- 为了处理响应，我们还定义了一个监听器，用于处理作为 '服务器到客户端' 类型接收的房间消息。
- 这个监听器在屏幕上显示从服务器接收到的首都城市名称。如果有多个首都城市，它们将在屏幕上显示，并用逗号分隔。



**服务器代码**

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

export default class extends Sandbox {

    onCreate(options: SandboxOptions) {
        // 处理接收到的 'client-to-server' 类型的消息
        this.onMessage("client-to-server", (client, message) => {
            console.log(`客户端 ${client.userId} 发送请求。消息: ${message}`);
            this.findCapitalCity(message, client);
        });
    }

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

    findCapitalCity(countryName: string, client: SandboxPlayer) {
        // 使用 countryName 作为路径参数请求 API
        // countryName 是客户端作为房间消息发送的
        HttpService.getAsync(`https://restcountries.com/v3.1/name/${countryName}?fields=capital`)
            .then((httpResponse) => {
                if (httpResponse.statusCode != 200) {
                    throw (`API 错误: ${httpResponse.statusCode} ${httpResponse.statusText}`);
                }
                return httpResponse.response;
            })
            .then((response) => {
                let countries = JSON.parse(response);
                if (!Array.isArray(countries) || !countries.length) {
                    throw (`API 错误: 响应数据无效`);
                }
                let country = countries[0];
                if (!country.capital) {
                    throw (`API 错误: 'capital' 字段无效`);
                }
                // 向客户端发送 'server-to-client' 类型的消息
                // 消息是一个包含首都城市名称的对象
                client.send("server-to-client", {
                    "capitals": country.capital
                });
            })
            .catch((reason) => {
                console.log("API 请求错误");
                console.log(reason);
            });
    }
}
```

代码描述：

- 我们定义一个监听器，当接收到 'client-to-server' 类型的 Multiplay Room Message 时，调用 `findCapitalCity`。
- 我们使用国家名称构建 Multiplay Room Message 的地址，并进行 `getAsync` 调用。
- 如果 `getAsync` 调用成功，响应将像之前的示例一样处理。
- 从 restcountries API 的响应中获得的首都城市名称作为 'server-to-client' 类型的 Room Message 发送给客户端。



### POST请求

最后，让我们使用 `HttpService.postAsync`.

[postman-echo](https://postman-echo.com/) 是一个提供结构化响应的服务，显示从网络请求中接收到的内容，使其有效地验证客户端是否正确配置了请求。

通过这个例子，我们将设置一个带有查询参数、请求体和头部的POST请求，并确保请求配置正确。

设置 Multiplay，然后打开 World.multiplay/index.ts 并按如下方式编写服务器脚本。

```typescript
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 端点和查询参数
            "https://postman-echo.com/post?argKey=argValue",
            // JSON 请求体
            { "dataKey": "dataValue" },
            // 请求内容类型
            HttpContentType.ApplicationJson,
            // HTTP 头
            { "header-key": "header-value" })
            .then((httpResponse) => {
                if (httpResponse.statusCode != 200) {
                    throw (`API 错误: ${httpResponse.statusCode} ${httpResponse.statusText}`);
                }
                return httpResponse.response;
            })
            .then((response) => {
                let parsed = JSON.parse(response);
                console.log(`查询参数: argKey:${parsed.args.argKey}`);
                console.log(`请求体: dataKey:${parsed.data.dataKey}`);
                console.log(`请求头: header-key:${parsed.headers["header-key"]}`)
            })
            .catch((reason) => {
                console.log("API 请求错误");
                console.log(reason);
            });
    }
}
```

代码描述：

- 当客户端连接到Multiplay Room时，`echoPost`函数从`onJoin`触发器被调用。
- 在发起`postAsync`请求时：
  - 在第一个参数中，我们构造带有查询参数的URL字符串。
  - 在第二个参数中，我们设置请求体内容。
  - 在第四个参数中，我们配置请求头。
  - 为了指定请求体为JSON格式，我们在第三个参数中设置'application/json'的Content-Type。
- 如果`postAsync`调用成功，我们可以在`httpResponse`对象中访问`then`回调。
- 我们解析`httpResponse.response`以将API响应的JSON格式转换为对象。
- 参考API响应的结构，我们使用控制台输出验证我们的HTTP请求的查询参数、请求体和请求头是否正确配置。

当您在Unity编辑器中运行Multiplay服务器并播放场景时，控制台将显示以下内容。

```text
查询参数：argKey:argValue
请求体：dataKey:dataValue
请求头：header-key:header-value
```

