Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Using UnityWebRequest in Editor Tools

Discussion in 'Multiplayer' started by kromenak, Apr 13, 2016.

  1. kromenak

    kromenak

    Joined:
    Feb 9, 2011
    Posts:
    270
    Overall, I'm pretty happy with the UnityWebRequest API for sending and receiving RESTful HTTP requests. One issue I'm running into, however, is using it to access a web API at edit-time (say, in a custom editor tool).

    For my use case, we want to convert some ScriptableObject assets into JSON and send them to our server through a REST API using POST.

    UnityWebRequest.Send() returns an AsyncOperation, which you can "yield on" in a Coroutine to asynchronously retrieve the result. However, there's no way to run Coroutines in an editor tool, correct? So, would I just need to block until the AsyncOperation returned from Send() says it's done? But would it ever say it's done if I blocked like that? Do I need to send the request from a separate thread in this case?

    It seems like the UnityWebRequest could easily support an asynchronous callback mechanism (perhaps the Send() call has an override that takes a callback, which is called when the request completes). But barring that, I'm not aware of an easy/effective way to make use of an AsyncOperation API in the editor. Any suggestions?

    As an alternative, I'm looking at the C# HttpWebRequest class, buuuut it would be cool if the UnityWebRequest could work.
     
    Zamaroht likes this.
  2. crafTDev

    crafTDev

    Joined:
    Nov 5, 2008
    Posts:
    1,820
    Hey,

    Ever figure out how to use REST in editor?

    Thanks,
    jrDev
     
  3. kromenak

    kromenak

    Joined:
    Feb 9, 2011
    Posts:
    270
    @jrDev, I guess the silence on this question indicates that there isn't a good way to use UnityWebRequest in editor...whoops.

    I ended up using the .NET HttpWebRequest class instead:
    Code (CSharp):
    1. // Create the POST request.
    2.         HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destinationUrl);
    3.         request.Method = "PUT";
    4.  
    5.         // Content type is JSON.
    6.         request.ContentType = "application/json";
    7.  
    8.         // Fill body.
    9.         byte[] contentBytes = new UTF8Encoding().GetBytes(data);
    10.         request.ContentLength = contentBytes.LongLength;
    11.         request.GetRequestStream().Write(contentBytes, 0, contentBytes.Length);
    12.  
    13.         try
    14.         {
    15.             using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    16.             {
    17.                 Debug.Log("Publish Response: " + (int)response.StatusCode + ", " + response.StatusDescription);
    18.                 if((int)response.StatusCode == 200)
    19.                 {
    20.                     SetEnvironmentVersion(version);
    21.                 }
    22.             }
    23.         }
    24.         catch(Exception e)
    25.         {
    26.             Debug.LogError(e.ToString());
    27.         }
     
    afonseca likes this.
  4. mlawrence

    mlawrence

    Joined:
    Dec 1, 2015
    Posts:
    1
    This may not be an elegant answer, but you can block based on https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest-responseCode.html.

    responseCode returns -1 until the Send operation is completed.

    Code (CSharp):
    1.  
    2. using(UnityWebRequest www = UnityWebRequest.Get(url)) {
    3.     www.Send();
    4.  
    5.     while (www.responseCode == -1) {
    6.         //do something, or nothing while blocking
    7.     }
    8.     if(www.isError) {
    9.         Debug.Log(www.error);
    10.     }
    11.     else {
    12.         //Show results as text
    13.         Debug.Log(www.responseCode.ToString());
    14.         //process downloadHandler.text
    15.     }
    16. }
    17.  
     
    Karreg likes this.
  5. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    Last edited: Aug 30, 2016
    rdjadu, slaczky, afonseca and 2 others like this.
  6. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    There is a bug introduced in Unity 5.5 where UnityWebRequests calls will not work on Editor (except if the editor is on Play mode, as in, Playing an scene or game).

    Is reported as (Case 858932) UnityWebRequest on Editor only works on Play Mode.

    However more than a week passed since the report, and no news from Unity yet.
     
  7. erich202

    erich202

    Joined:
    Sep 24, 2016
    Posts:
    38
  8. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    @EricMuyser they have merged the issue with another one and closed my report. Is supossedly being worked. I have been updating on this issue here: Google Sheets For Unity
     
    erich202 likes this.
  9. tcz8

    tcz8

    Joined:
    Aug 20, 2015
    Posts:
    504
    That bug is still present and I beleive this is the issue you were looking for: https://issuetracker.unity3d.com/is...dlerscript-gets-paused-if-scene-is-not-played

    I have a webrequest attached to a button in an EditorWindow, it doesnt log errors but always return an empty string.
    If I go int play mode before pressing the button, then it works fine.

    Update:
    If you put the attribute [UnityEngine.ExecuteInEditMode] on top of your EditorWindow class it will make it work. I'm just not sure if I want to have my script run in edit mode all the time.
     
    Last edited: Apr 5, 2019
  10. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    @tcz8 heya, thanks for the link to the report. I have long moved to later Unity releases. Right now using 2018.3.2f1 and it works right on the editor using the EditorApplication.update method described in the blog.
     
  11. Mikilo

    Mikilo

    Joined:
    Jan 29, 2013
    Posts:
    694
    Hi, here is an easier implementation that works from 5.6 to 2019.

    Code (CSharp):
    1. public static Utility
    2. {
    3.    public static void       StartBackgroundTask(IEnumerator update, Action end = null)
    4.    {
    5.        EditorApplication.CallbackFunction   closureCallback = null;
    6.  
    7.        closureCallback = () =>
    8.        {
    9.            try
    10.            {
    11.                if (update.MoveNext() == false)
    12.                {
    13.                    if (end != null)
    14.                        end();
    15.                    EditorApplication.update -= closureCallback;
    16.                }
    17.            }
    18.            catch (Exception ex)
    19.            {
    20.                if (end != null)
    21.                    end();
    22.                Debug.LogException(ex);
    23.                EditorApplication.update -= closureCallback;
    24.            }
    25.        };
    26.  
    27.        EditorApplication.update += closureCallback;
    28.    }
    29. }


    Usage :
    Code (CSharp):
    1. Utility.StartBackgroundTask(Test());
    Where Test() can be :
    Code (CSharp):
    1. private IEnumerator   Test()
    2. {
    3.    using (UnityWebRequest w = UnityWebRequest.Get("https://www.google.com"))
    4.    {
    5.        yield return w.SendWebRequest();
    6.  
    7.        while (w.isDone == false)
    8.            yield return null;
    9.  
    10.        Debug.Log(w.downloadHandler.text);
    11.    }
    12. }
     
    Last edited: Jun 28, 2019
  12. FloBeber

    FloBeber

    Joined:
    Jun 9, 2015
    Posts:
    166
    That's great man. Thanks a lot for sharing.
     
    Mikilo likes this.
  13. JiawenYu

    JiawenYu

    Joined:
    Dec 25, 2019
    Posts:
    3
    It's great! Thanks
     
    mchts and Mikilo like this.
  14. crafTDev

    crafTDev

    Joined:
    Nov 5, 2008
    Posts:
    1,820
    Hello,

    Just what I needed!

    Thanks,
    jrDev
     
  15. Andrey-Postelzhuk

    Andrey-Postelzhuk

    Joined:
    Nov 26, 2013
    Posts:
    75
    It's possible to use `completed` event from `AsyncOperation`
    Code (CSharp):
    1.  
    2.             var req = UnityWebRequest.Get("http://google.com");
    3.             var op = req.SendWebRequest();
    4.             op.completed += (aop) =>
    5.             {
    6.                 Debug.Log("completed: " + req.downloadHandler.text);
    7.                 req.Dispose();
    8.             };
     
    anand_patel and adamgolden like this.
  16. Chico3001

    Chico3001

    Joined:
    Mar 10, 2018
    Posts:
    8
    Unity just released Editor Corutines Utility on the Package Manager, now is possible to do something like this:

    Code (CSharp):
    1. using System.Collections;
    2. using Unity.EditorCoroutines.Editor;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.Networking;
    6.  
    7.  
    8. // https://docs.unity3d.com/Packages/com.unity.editorcoroutines@1.0/api/Unity.EditorCoroutines.Editor.EditorCoroutineUtility.html
    9. public class TestScript : EditorWindow
    10. {
    11.  
    12.     [MenuItem("Test/Test")]
    13.     public static void ShowWindow()
    14.     {
    15.         GetWindow<TestScript>("Open Window");
    16.     }
    17.  
    18.     private void OnGUI()
    19.     {
    20.         if (GUILayout.Button("Test Corutine"))
    21.         {
    22.             EditorCoroutineUtility.StartCoroutine(GetJSON(), this);
    23.         }
    24.     }
    25.  
    26.     protected IEnumerator GetJSON()
    27.     {
    28.         using (UnityWebRequest www = UnityWebRequest.Get("http://localhost/"))
    29.         {
    30.             yield return www.SendWebRequest();
    31.  
    32.             if (www.isNetworkError || www.isHttpError)
    33.             {
    34.                 Debug.LogError(www.error);
    35.             }
    36.             else
    37.             {
    38.                 Debug.Log(www.downloadHandler.text);
    39.             }
    40.         }
    41.     }
    42. }
     
    airespt likes this.
  17. Mikilo

    Mikilo

    Joined:
    Jan 29, 2013
    Posts:
    694
    Having to import a package for such a simple feature... That's freaking sad...
     
    Chico3001 likes this.
  18. Ban-jason

    Ban-jason

    Joined:
    Nov 11, 2014
    Posts:
    8

    I've tried the two methods above, and I recommand Editor Corutines Utility. Because somtimes the first method doesn't work. And I can't find why ? that's sad.
     
  19. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    169
    I use callback to handle it.
    Code (CSharp):
    1.            _postRequest = new UnityWebRequest(apiAddress, "POST");
    2.             var asyncOp = _postRequest.SendWebRequest();
    3.             asyncOp.completed += OnPostRequestCompleted;
    Code (CSharp):
    1. private void OnPostRequestCompleted(AsyncOperation op)
    2.         {
    3.             var postRequest = (op as UnityWebRequestAsyncOperation).webRequest;
    4.             if (postRequest.result != UnityWebRequest.Result.Success)
    5.             {
    6.                 Debug.Log(postRequest.error);
    7.                 _postRequest.Dispose();
    8.             }
    9.            else
    10.            {
    11.                _postRequest.Dispose();
    12.             }
    13.          }
    14.