Unity 提供了 UnityWebRequest 这个和网络有关的类,我们可以借助它实现数据的请求和响应的接收。想了解详情可参考 Unity 官方文档中相关的 API 介绍:UnityWebRequest
那么下面就来为大家演示一下如何利用 UnityWebRequest 发送 Json 数据格式的 Post 请求。后端的部分我是用基于 Java 的 SpringBoot 框架搭建的程序,对应的接口用了 @RequestBody 注解来接收前端传来的 Json 请求参数。这里就不具体演示后端怎么写了,本篇博客还是把重点放在 Unity 前端的实现方法上,总的来说有两种实现方式,但不论是哪一种,思路都是一样的,只是代码的实现上有些不同。
如果在百度上搜,可能大部分会搜到这种实现方式,我先附上代码,然后再具体解释:
private void Start() {
string url="xxx";
string json="一个Json格式的数据,这里大家替换成自己想要测试的Json数据";
StartCoroutine(I_RequestByJsonBodyPost(url,json));
}
private static IEnumerator I_RequestByJsonBodyPost(string url, string json)
{
UnityWebRequest www = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
DownloadHandler downloadHandler = new DownloadHandlerBuffer();
www.downloadHandler = downloadHandler;
www.SetRequestHeader("Content-Type", "application/json;charset=utf-8");
byte[] bodyRaw = Encoding.UTF8.GetBytes(json);
www.uploadHandler = new UploadHandlerRaw(bodyRaw);
yield return www.SendWebRequest();
Debug.Log(www.downloadHandler.text);
}
(注:Encoding 位于 System.Text 命名空间下,使用前要先 using System.Text)
这里假设大家已经得到了 Json 数据。而在实际开发中,基本上是要把一个对象转化成 Json 字符串。Unity 中也有许多Json 与对象互相转化的解决方案,如 Newstonsoft.json , LitJson, JsonUtility 等。
重点讲解:
1️⃣ UnityWebRequest 结合协程一起使用是比较常见的。SendWebRequest() 这个方法就是开始与远程服务器通信,然后会暂停协程,等到完成通信或系统错误后才会继续执行 yield return 后面的代码。
2️⃣ UnityWebRequest.kHttpVerbPOST 是 UnityWebRequest 类的一个静态常量,值为 “POST”
3️⃣ 为了保证前端发送的是 Json 格式的数据,让后端能够正常地解析,必须要把请求头中的 Content-Type 设为 “application/json” ,因此要有这段代码:
www.SetRequestHeader("Content-Type", "application/json;charset=utf-8");
注: 为了保证字符不乱码,我改成了"application/json;charset=utf-8"
4️⃣ 为了能够传送数据给后端, UnityWebRequest 提供了 uploadHandler 这个字段用于管理请求体的数据。我们这里使用的是原生(Raw)的 Json 数据,因此要使用 UploadHandlerRaw 类(继承自 UploadHandler 类),利用 System.Text.Encoding.UTF8.GetBytes 将 Json 字符串转化成字节流传入 UploadHandlerRaw 类的构造函数。
5️⃣ 为了能够接收后端的响应, UnityWebRequest 提供了 downloadHandler 这个字段用于下载后端返回的响应,我们可以用 downloadHandler 的 text 字段得到响应的字符串信息。
⭐ 总结一下,大致的思路是:
① 实例化 UnityWebRequest,设置为 Post 请求
② 设置 Content-Type
③ 配置 uploadHandler 和 downloadHandler
要想发送 Post 请求,我们可以用 UnityWebRequest.Post 这个 API。
可以看到这个 API 有 9 个重载,Json 格式的数据可以是个字符串,那么这里选用上图中的第一个重载。
public static UnityWebRequest Post(string uri, string postData);
我们先来看看这个 API 的官方介绍:
这个 API 内部的实现方法其实大部分和我们刚刚介绍的方法一是类似的,可以说它帮我们封装了一些东西。这里有几个重点我先翻译下:
1️⃣ 这个方法设置的 Content-Type 默认是 application/x-www-form-urlencoded,因此要想发送 Json 数据,我们也要手动把 Content-Type 设成 application/json;charset=utf-8
2️⃣ 这个方法已经为我们实例化了一个 downloadHandler
3️⃣ 这个方法也为我们实例化了一个 uploadHandler,并且它能将数据转化为字节流,不过注意一下第一句话,数据会先被 escaped ,这里先卖个关子,待会儿会详细介绍这个 escaped,也是这一部分的重点(里面有坑)。
那么既然为我们在内部实现了 uploadHandler 和 downloadHandler 的逻辑,是不是使用起来就更简单了呢?
来看下面这段代码:
private static IEnumerator I_RequestByJsonBodyPost(string url, string json)
{
UnityWebRequest www = UnityWebRequest.Post(url, json);
www.SetRequestHeader("Content-Type", "application/json;charset=utf-8");
yield return www.SendWebRequest();
Debug.Log(www.downloadHandler.text);
}
这时候,查看后端的日志,发现报了这个错:
报错信息大致是后端无法读取前端传递的数据格式,而且可以发现,为什么后端收到的数据中会有个 “%”,前端传的不是 Json 格式的数据吗?哪来的 % 呢?
不过看到这个 %,我大致可以猜到是进行了某种字符编码。现在我们再回到刚刚留下来的 escaped 问题。回看官方 API 文档,重点关注这句话:
可以看到我们传进去的字符串会被 URLEncoded。那么这个 escaped 的过程,就是对 postData 进行了 URLEncode。因为这个 API 默认把 Content-Type 当作 application/x-www-form-urlencoded,而 URLEncode 是为这种 Content-Type 准备的,我们可以看看 URLEncode 的相关解释:
URL编码(URL encoding),也称作百分号编码(Percent-encoding), 是特定上下文的统一资源定位符 (URL)的编码机制。适用于统一资源标识符(URI)的编码,也用于为"application/x-www-form-urlencoded" MIME准备数据, 因为它用于通过HTTP的请求操作(request)提交HTML表单数据。
因此这个方法在内部将我们的 Json 数据进行了 URL 编码。我们可以用 URL 编码转换工具试一试:(网址:https://www.gjk.cn/urlencode)
URL 编码后:
这也解释了为什么后端会收到一个%。但因为后端对应的接口设置了只能解析 Json 格式的数据,它明显不认这个 URL 编码,所以报错了。
我们也可以看看 Unity 内部是如何实现这个 Post 方法的(github链接)
可以看到在把 postData 转换为 byte 数组前,它多了一句 URL 编码的转换。
那么如何解决这个问题呢?
我们只能手动创建一个 uploadHandler,覆盖掉原来的 uploadHandler。
private static IEnumerator I_RequestByJsonBodyPost(string url, string json)
{
UnityWebRequest www = UnityWebRequest.Post(url, json);
www.SetRequestHeader("Content-Type", "application/json;charset=utf-8");
byte[] bodyRaw = Encoding.UTF8.GetBytes(json);
www.uploadHandler = new UploadHandlerRaw(bodyRaw);
yield return www.SendWebRequest();
Debug.Log(www.downloadHandler.text);
}
后记:
本篇介绍的方法一在网上能够搜到很多类似的介绍。但是当时我也有点纳闷,UnityWebRequest.Post 看起来似乎更加简洁,为什么很少的文章是介绍用 UnityWebRequest.Post 来发送 Json 数据呢?经过尝试我得到了本篇文章里提及的后端的报错。而我之前也一直没注意到 UnityWebRequest.Post(string uri, string postData) 这个方法在内部居然会先进行 URL 编码,也在网上搜了好久,虽然最后总算搜到了相关的资料,但是感觉目前网上对这个问题的解答不是很多。于是就想写这篇博客做一个总结,希望能够帮到更多的小伙伴。