一、unity之C#调用Java的webservice接口的坑
初识Unity
虽然前些年我是C#出身的女程序员,后来转成Java方向了,但是我对unity的后台脚本那是一丁点儿不知道啊,但是最近公司在做工厂方面的三维应用,而负责弄三维的UI大佬对开发语言那是一窍不通的小白鼠啊,那怎么办?只有我挺身而出(谁让我是公司唯一的一个会C#的人呢),这里差点被鸭梨山大压得喘不上气啊~~~~~
坑One
以下是Java里的webservice调用方式:
XXX是自己写的jar包
package com.XXX.util;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public class WebServiceClient2 {
public static void main(String[] args) {
try {
// 1 指定WebService服务的请求地址:
String wsUrl = "http://10.192.154.206:9001/XXX/services/WebScadaServiceAPI";
// 2 创建URL:
URL url = new URL(wsUrl);
// 3 建立连接,并将连接强转为Http连接
URLConnection conn = url.openConnection();
HttpURLConnection con = (HttpURLConnection) conn;
// 4,设置请求方式和请求头:
con.setDoInput(true); // 是否有入参
con.setDoOutput(true); // 是否有出参
con.setRequestMethod("POST"); // 设置请求方式
con.setRequestProperty("content-type", "text/xml;charset=UTF-8");
// 5,手动构造请求体
String requestBody = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://service.XXX.com/\">";
requestBody += "<soapenv:Header/>";
requestBody += "<soapenv:Body>";
requestBody += "<ser:getPointsData>";
requestBody += "<kdName>yd_123456_Ia</kdName>";
requestBody += "</ser:getPointsData>";
requestBody += "</soapenv:Body>";
requestBody += "</soapenv:Envelope>";
// 6,通过流的方式将请求体发送出去:
OutputStream out = con.getOutputStream();
out.write(requestBody.getBytes());
out.close();
// 7,服务端返回正常:
int code = con.getResponseCode();
if (code == 200) {// 服务端返回正常
InputStream is = con.getInputStream();
byte[] b = new byte[1024];
StringBuffer sb = new StringBuffer();
int len = 0;
while ((len = is.read(b)) != -1) {
String str = new String(b, 0, len, "UTF-8");
sb.append(str);
}
System.out.println(sb.toString());
is.close();
}
con.disconnect();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
想着java那是通过soapui的方式来请求的,那unity也可以用这样的方式通过C#来实现啊,soeasy啊!码上我的C#代码!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text;
using System.Xml;
using UnityEngine.UI;
using System.IO;
using System.Net;
using LitJson;
using System.Linq;
using UnityEngine.Networking;
public class webtest001 : MonoBehaviour
{
public Text yd_123456_Ia;
public Text yd_123456_Ib;
void Start()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("1", "yd_123456_Ia");
dic.Add("2", "yd_123456_Ib");
StartCoroutine(callWebService("getPointsData", dic));
}
public string callWebService(string methodName, Dictionary<string, string> param)
{
Dictionary<string, string> dic = new Dictionary<string, string>();
///获取请求数据
byte[] data = getRequestDataALL(methodName, param);
String url = "http://10.192.154.206:9001/XXX/services/WebScadaServiceAPI HTTP/1.1";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "text/xml; charset=utf-8";
string mSoapAction = "";//"http://tempuri.org/" + methodName;
request.Headers.Add("SOAPAction", mSoapAction);
request.ContentLength = data.Length;
Stream rStream = request.GetRequestStream();
rStream.Write(data, 0, data.Length);
rStream.Close();
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string result = reader.ReadToEnd();
dataStream.Close();
response.Close();
result = result.Replace(""","\"");
object obj="";
String str=FormatXML(result);
if (str.Equals("") == false)
{
str = str.Substring(0, str.Length - 1);
JsonData jsonData = JsonMapper.ToObject(str);
string res = jsonData["result"].ToJson();
jsonData = JsonMapper.ToObject(res);
foreach (JsonData item in jsonData)
{
obj+= item["kdData"].ToString();
dic.Add(item["kdName"].ToString(), item["kdData"].ToString());
//print(item["kdData"] + ":" + item["alarmStatue"] + ":" + item["kdName"]);
}
}
string d1=dic.SingleOrDefault(o => o.Key == "yd_123456_Ia").Value;
string d2 = dic.SingleOrDefault(o => o.Key == "yd_123456_Ib").Value;
yd_WNSJD150_Ia.GetComponent<Text>().text = String.Format("设备效率:{0}", d1);
yd_WNSJD150_Ib.GetComponent<Text>().text = String.Format("冷却温度比:{0}", d2);
return result;
}
public string FormatXML(string str)
{
String val = "";
XmlDocument doc = new XmlDocument();
doc.LoadXml(str);
XmlNodeList nodeList = doc.GetElementsByTagName("soap:Body");
StringBuilder nameList = new StringBuilder();
//循环xml数据
foreach (XmlNode xmlNode in nodeList)
{
XmlNodeList childList = xmlNode.ChildNodes; //取得row下的子节点集合
foreach (XmlNode item in childList)
{
String name = item.InnerXml;
if (name.Contains("return"))
{
val+=item.InnerText+",";
}
}
}
return val;
}
public byte[] getRequestDataALL(string methodName, Dictionary<string, string> param)
{
string str = "";
StringBuilder requestBuider = new StringBuilder();
requestBuider.Append("<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:ser='http://service.XXX.XXX.com/'>");
requestBuider.Append("<soapenv:Header/>");
requestBuider.Append("<soapenv:Body>");
requestBuider.Append("<ser:").Append(methodName).Append(">");
requestBuider.Append("<kdName>");
foreach (KeyValuePair<string, string> item in param)
{
str+=item.Value+",";
}
str = str.Substring(0,str.Length-1);
requestBuider.Append(str);
requestBuider.Append("</kdName>");
requestBuider.Append("</ser:").Append(methodName).Append(">");
requestBuider.Append("</soapenv:Body>");
requestBuider.Append("</soapenv:Envelope>");
string val = requestBuider.ToString();
byte[] data = Encoding.UTF8.GetBytes(val);
return data;
}
}
然后在unity中进行运行的时候,数据请求过来了,暗自窃喜成功了,然而UI大佬那里发布出来生成Webgl的时候却是个空白,画面啥数据都没有!
后来UI大佬自己百度了下原因,说与以下网页遇到的问题是一样的,得用unity自己的请求方式才行!
https://www.codercto.com/a/72962.html
分析了下:首先是Http请求的问题,我最开始想的是,直接用C#里的写法,编辑器里测试毫无问题,但是一打包出来就不行,会报出 SystemException: Thread creation failed. 的错误,无奈只能用Unity自己的 请求方式,例如UnityWebRequest 、WWW 等。
我参考了下面的网址:https://www.cnblogs.com/fyluyg/p/6047819.html
后来发现www已经过时,后来就用了UnityWebRequest方式
using LitJson;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class WebGLTest : MonoBehaviour
{
enum Request_Type
{
POST,
SOAP,
}
public Text myText;
public Text txtNum;
public Text txtNum02;
public float timer = 10.0f;
public string mu_url = "http://10.192.154.206:9001/XXX/unty3DServiceAPI";
//声明的协程对象
IEnumerator ie;
// Start is called before the first frame update
void Start()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("1", "yd_123456_Ia");
dic.Add("2", "yd_123456_Ib");
JsonData data = new JsonData();
data["kdName"] = "yd_123456_Ia,yd_123456_Ib";//传JSON格式参数定义,具体根据提供的JOSN数据为准
ie = IPostData(mu_url, data);
StartCoroutine(ie);
}
// Update is called once per frame
void Update()
{
JsonData data = new JsonData();
data["kdName"] = "yd_123456_Ia,yd_123456_Ib";
timer -= Time.deltaTime;
if (timer <= 0)
{
Debug.Log(string.Format("Timer1 is up !!! time=${0}", Time.time));
timer = 10.0f;
StartCoroutine(IPostData(mu_url, data));
}
}
IEnumerator IPostData(string m_url, JsonData data)
{
using (UnityWebRequest webRequest = new UnityWebRequest(m_url, UnityWebRequest.kHttpVerbPOST))
{
byte[] postBytes = System.Text.Encoding.UTF8.GetBytes(data.ToJson());
webRequest.uploadHandler = (UploadHandler)new UploadHandlerRaw(postBytes);
webRequest.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
webRequest.SetRequestHeader("Content-Type", "application/json");
webRequest.SendWebRequest();
if (webRequest.isNetworkError || webRequest.isHttpError)
{
Debug.LogError(webRequest.error + webRequest.isHttpError);
}
while (webRequest.responseCode != 200)
{
yield return null;//这里一定要写到调用接口不通的情况下才迭代返回,不然update那里实时刷新调取进来这个方法只会调取一次
}
if(webRequest.responseCode == 200)
{
string st = webRequest.downloadHandler.text.Replace("\\", "");
print(st.Substring(1, st.Length - 1));
Dictionary<string, string> dic = parseJson(st.Substring(1, st.Length - 1));
string d1 = dic.SingleOrDefault(o => o.Key == "yd_123456_Ia").Value;
string d2 = dic.SingleOrDefault(o => o.Key == "yd_123456_Ib").Value;
//unity控件绑定值方式1
//GameObject t = GameObject.Find("myText");
//t.GetComponent<Text>().text = d1;//有效
//GameObject txtNum = GameObject.Find("txtNum");
//txtNum.GetComponent<Text>().text = d2;//有效
//unity控件绑定值方式2(需要在unity的inspector中绑定,后面会说到)
myText.text = d1;
txtNum.text = d2;
}
}
}
private Dictionary<string, string> parseJson(string data)
{
Dictionary<string, string> dic = new Dictionary<string, string>();
JsonData jsonData = JsonMapper.ToObject(data);
string errorCode = jsonData["errorCode"].ToString();
if (errorCode.Equals("0"))
{
string res = jsonData["result"].ToJson();
jsonData = JsonMapper.ToObject(res);
foreach (JsonData item in jsonData)
{
Debug.Log(item["kdData"].ToString());
dic.Add(item["kdName"].ToString(), item["kdData"].ToString());
}
}
return dic;
}
}
坑Two
码完上面的代码,发现接口调取是通的(返回状态是200),但是为何没有数据呢?postman测试接口工具也是可以正常有返回数据的,为何这里C#就返回没有数据?头疼了一天多后来通过debug模式才发现是java那里post方式获取参数没有通过流模式读取,而是request方式来获取。
改成第一个红框中请求方式,发现返回过来值就有了,于是就在unity运行没有问题。开始发布webgl看看有没有问题,然而天有不测风云,又出现:**CORS 头缺少 ‘Access-Control-Allow-Origin’**问题
坑Three
我各种百度,google,基本上都说是加上上图中的**Access-Control-Allow-Origin:***参数设置或者通过Firefox浏览器将 security.fileuri.strict_origin_policy 修改,但是在新的Firefox浏览器不支持,于是其转换方向,向浏览器跨域问题方向百度,居然真的被我找到答案。
https://blog.csdn.net/nju_zjy/article/details/108870385
使用firefox浏览器插件来实现跨域,例如CORS-EVERYWHERE
https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/
它是可用的。它的github地址是https://github.com/spenibus/cors-everywhere-firefox-addon。
装完插件,在运行webgl下面的index.html记得点击下firefox浏览器右上角的CORS-EVERYWHERE插件
绿色代表插件启用,或者自行前往设置插件默认启用
好了,关于unity中数据请求并实时刷新的实现方式及webgl中跨域问题的坑完美解决~~(现在百度上很多关于webgl的浏览器跨域问题求助帖都没办法解决,希望他们可以看到我的记录吧)
后续再更新如何在unity中使用XCharts及实时更新数锯,记得关注我哦~~~