Skip to main content
 首页 » 编程设计

c#之UnityWebRequest 和/或 HttpWebRequest 在 Android 上使用 PUT 给出 403

2024年01月31日27lori

((我还在 Unity Answers 和 GameDev 上发布了这个,但在这里发帖以防任何 C# 网络负责人指出我的 HttpWebRequest 代码中的任何明显错误...))

我试图通过他们的 SDK 将通过我的游戏捕获的图像发布到 Facebook,并且由于它需要 URI,我编写了一个简单的 AWS Lambda 函数来获取字节数组并将其上传到 AWS 存储桶。 AWS 函数进行调用,然后返回一个 URL 以使用图像数据调用 PUT ... 所以想法是,如果响应是 200,则使用该 URL 发布到 FB。

但是 - 使用 Unity 5.2.x 到 5.3.4,以及 Android 4.4 - 6.1 - 它总是给我一个 403 响应,同时在 iOS 上运行良好。

所以,我有一个方法接受一个 byte[] 然后这样做:

    WWW w = new WWW("https://my-amazon-function-to-call"); 
    yield return w; 
    if (!string.IsNullOrEmpty(w.error)) { 
        Debug.LogError(w.error); 
    } 
    else  
    { 
             var dict = Json.Deserialize(w.text) as Dictionary<string,object>; 
             string oneTimeUploadUrl = (string)dict["oneTimeUploadUrl"]; // Private URL 
             string resultUrl = (string)dict["resultUrl"]; // Public URL 
 
            UnityWebRequest aws = UnityWebRequest.Put(oneTimeUploadUrl, bytes); 
            yield return aws.Send(); 
            if(aws.isError)  
            { 
                Debug.LogError("AWS ERROR: " + aws.error); 
            } 
            if(aws.responseCode == 200) 
            { 
                FeedShare(new Uri(resultUrl), _cachedMessage);    // FB call 
            } 
    } 

很简单,对吧? 在 iOS 上,是的。但是 Android 它不断给我 PUT 操作的 403 响应。

所以,我已经开始将它包装在特定于 iOS 的 #ifdef 中,并尝试为 Android 尝试一些更原生的 C-Sharp-ish ......例如:

    WWW w = new WWW("https://my-amazon-function-to-call"); // tried pure old Http too 
    yield return w; 
    if (!string.IsNullOrEmpty(w.error)) { 
        Debug.LogError(w.error); 
    } 
    else { 
        var dict = Json.Deserialize(w.text) as Dictionary<string,object>; 
        string oneTimeUploadUrl = (string)dict["oneTimeUploadUrl"]; 
        string resultUrl = (string)dict["resultUrl"]; 
 
    #if UNITY_IPHONE 
        UnityWebRequest aws = UnityWebRequest.Put(oneTimeUploadUrl, bytes); 
        yield return aws.Send(); 
        if(aws.isError)  
        { 
            Debug.LogError("AWS ERROR: " + aws.error); 
        } 
        if(aws.responseCode == 200) 
        { 
            FeedShare(new Uri(resultUrl), _cachedMessage);     
        } 
    #else              
 
    // Various Security Callback Tests 
        //ServicePointManager.ServerCertificateValidationCallback = (p1, p2, p3, p4) => true; 
        //ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback; 
        ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications); 
 
 
        HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(oneTimeUploadUrl); 
        //wreq.AuthenticationLevel = System.Net.Security.AuthenticationLevel.None; 
        //wreq.PreAuthenticate = false; 
        wreq.Method = "PUT"; 
        wreq.ContentType = "image/png"; 
        wreq.ContentLength = bytes.Length; 
 
        Stream newStream = wreq.GetRequestStream(); 
        newStream.Write(bytes, 0, bytes.Length); 
        newStream.Close(); 
 
        HttpWebResponse response = (HttpWebResponse)wreq.GetResponse(); 
        if((int)response.StatusCode == 200) 
        { 
            FeedShare(new Uri(resultUrl), _cachedMessage); 
        } 
     #endif 
    } 

...但这也给了我一个 403。我尝试了几个不同的选项,正如您从注释掉的代码中看到的那样,但没有爱。作为记录,以下是用作 ServerCertificateValidationCallbacks 的 SSL 策略函数:

public bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) 
{ 
    return true; 
} 
 
public bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { 
    bool isOk = true; 
    if (sslPolicyErrors != SslPolicyErrors.None) { 
        for (int i=0; i<chain.ChainStatus.Length; i++) { 
            if (chain.ChainStatus [i].Status != X509ChainStatusFlags.RevocationStatusUnknown) { 
                chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; 
                chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; 
                chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0); 
                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; 
                bool chainIsValid = chain.Build ((X509Certificate2)certificate); 
                if (!chainIsValid) { 
                    isOk = false; 
                } 
            } 
        } 
    } 
    print("MyRemoteCertificateValidationCallback: " + isOk); 
    return isOk; 
} 

这是实际的 StackTrace:

04-01 11:14:57.325: I/Unity(22979):   
04-01 11:14:57.325: I/Unity(22979): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 37) 
04-01 11:14:57.825: I/Unity(22979): WebException: The remote server returned an error: (403) Forbidden. 
04-01 11:14:57.825: I/Unity(22979):   at System.Net.HttpWebRequest.CheckFinalStatus (System.Net.WebAsyncResult result) [0x00000] in <filename unknown>:0  
04-01 11:14:57.825: I/Unity(22979):   at System.Net.HttpWebRequest.SetResponseData (System.Net.WebConnectionData data) [0x00000] in <filename unknown>:0  
04-01 11:14:57.825: I/Unity(22979):   
04-01 11:14:57.825: I/Unity(22979): (Filename:  Line: -1) 

在这一点上,我完全被卡住了......我唯一的想法是也许写一个原生插件......但我很乐意不必须这样做......

想法???

请您参考如下方法:

我遇到了类似的问题,我总是得到 403。然后我发现我的服务器正在检查 Content-Type 和 HEADER 中的 >User-Agent。在我把它们放好之后,它就起作用了。我的代码是:

UnityWebRequest www = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST); 
byte[] body = new System.Text.UTF8Encoding().GetBytes(json_string); 
www.uploadHandler = (UploadHandler)new UploadHandlerRaw(body); 
www.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); 
www.SetRequestHeader("Content-Type", "application/json"); 
www.SetRequestHeader("User-Agent", "DefaultBrowser"); 
www.SetRequestHeader("Cookie", string.Format("DummyCookie")); 
www.chunkedTransfer = false; 
yield return www.Send();