Search Unity

Using UnityWebRequest in Editor Tools

Discussion in 'Connected Games' started by kromenak, Apr 13, 2016.

  1. kromenak

    kromenak

    Joined:
    Feb 9, 2011
    Posts:
    236
    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.
     
  2. jrDev

    jrDev

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

    Ever figure out how to use REST in editor?

    Thanks,
    jrDev
     
  3. kromenak

    kromenak

    Joined:
    Feb 9, 2011
    Posts:
    236
    @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.  
     
  5. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    615
    Last edited: Aug 30, 2016
    afonseca, -chris and Zaelot like this.
  6. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    615
    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. EricMuyser

    EricMuyser

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

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    615
    @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
     
    EricMuyser likes this.
  9. tcz8

    tcz8

    Joined:
    Aug 20, 2015
    Posts:
    196
    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:
    615
    @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:
    439
    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. }
     
    jrDev, 13Flo and JohnTube like this.
  12. 13Flo

    13Flo

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