Search Unity

UnityWebRequest (400 Bad Request) vs JavaScript

Discussion in 'Scripting' started by aer0ace, Apr 27, 2022.

  1. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    I'm running into an issue where I'm calling a UnityWebRequest GET call, which results in 400 Bad Request, but from a test webpage executing Javascript with the same exact url GET call, succeeds. What's weirder is that, my server actually does receive the call, and the function returns okay, but Unity reports back 400, and reports ProtocolError in my coroutine.

    Other notes, I am successful calling an earlier UnityWebRequest PUT without any problems.

    This leads me to believe I'm doing something wrong with my UnityWebRequest. I know it's not much to go off of, as I'm pretty new to web development. Anyone have any ideas where I should investigate this?
     
  2. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    you should show you code, you might not be waiting for it to complete or any other number of things
     
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,990
    If you get an HTTP response code 400 Bad Request, that's a server error code. However response codes starting with 4xx generally indicate a client error. 400 Bad Request can be returned by a server if you send some malformed request or if there's something else wrong in the request itself. Like @passerbycmc said you have to show how your request actually looks like. We don't know what your server expects, so that's another thing. Though without knowing how your request looks like we can't really help you besides telling you: You did something wrong ^^.
     
  4. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    It's pretty simplistic. Here's basically what I'm doing:
    Code (CSharp):
    1.         private void RequestData(int id)
    2.         {
    3.             string ip = "localhost";
    4.             int port = 4000;
    5.  
    6.             string uri = string.Format("http://{0}:{1}/api/command?id={2}", ip, port, id);
    7.  
    8.             StartCoroutine(GetWebRequest(uri));
    9.         }
    10.  
    11.         private IEnumerator GetWebRequest(string uri)
    12.         {
    13.             using (UnityWebRequest www = UnityWebRequest.Get(uri))
    14.             {
    15.                 yield return www.SendWebRequest();
    16.  
    17.                 string resultText;
    18.  
    19.                 if (www.result != UnityWebRequest.Result.Success)
    20.                 {
    21.                     resultText = www.error;
    22.                 }
    23.                 else
    24.                 {
    25.                     resultText = "Request successful.";
    26.                 }
    27.             }
    28.         }
     
    Last edited: Apr 27, 2022
  5. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    Thanks for the info. One part I haven't debugged yet is the server response. Some info on that: So, the server receives the request properly, and actually processes it properly. What's also weird is that the 400 Bad Request error came back to Unity BEFORE the server fully processed the request (because I had a breakpoint set there). I'll have to debug the server more.
     
  6. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    what happens if you just hit the endpoint with curl as a test
    Code (CSharp):
    1. curl http://localhost:4000/api/command?id=1
     
  7. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    The request succeeds and the server handles it properly. After debugging a bit more, I may actually have some problems with how I'm sending the request. I am actually using SignalR to receive a runtime event, and the callback of that event fires the UnityWebRequest. Somewhere along here is what's messing up the actual request. The request itself I believe is working properly.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    If SignalR is doing that callback anywhere except the main thread, it probably won't work for Unity.

    Delegate queue to marshal tasks from other threads to the main thread:

    https://forum.unity.com/threads/how...-everytime-it-is-called.1148354/#post-7370375

    Beyond that, here's the usual network debug coredump:

    Networking, UnityWebRequest, WWW, Postman, curl, WebAPI, etc:

    https://forum.unity.com/threads/using-unity-for-subscription-lists.1070663/#post-6910289

    https://forum.unity.com/threads/unity-web-request-acting-stupid.1096396/#post-7060150

    And setting up a proxy can be very helpful too, in order to compare traffic:

    https://support.unity.com/hc/en-us/articles/115002917683-Using-Charles-Proxy-with-Unity
     
  9. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    This is the direction I'm looking to approach this. Thanks for all the info!
     
    Kurt-Dekker likes this.
  10. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    Follow up, yep @Kurt-Dekker, I added your basic delegate queue, and that solved the problem. Thanks!
     
    Kurt-Dekker likes this.
  11. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    Actually I take that back. The first time it worked, and since then, it stopped working, and I get 400 again. I was actually getting this behavior before. It would work a few times, and then stop working with 400. Rebooting the computer actually seems the only way to get it to reset, because just shutting down the Unity client and server isn't enough. I have no idea why or when it starts to work again. It's something I do to get it to reset or something.
     
  12. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    The 400 is gonna be some kind of malformation of data most likely... do you have control of the endpoint? Can you check the logs?

    Otherwise you're gonna have to go the proxy way above until you nail down how the UWR differs and fix that difference.
     
  13. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    Yes, so I'll have to investigate there again.
     
  14. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,990
    As I already said above, it's a response code from the server. So the actual issue is detected on the server and the server is returning this error code. However as I said errors starting with 4 usually indicates an issue on the client side. Just like the common 404 which means that the resource / endpoint does not exist on the server. So the client tries to access something that isn't there... therefore a client error. Bad Request could mean anything. For example if a request requires a user ID or some other parameter but that value may be either missing or in the wrong range, the server may respond with a Bad Request error. If the request is fine but there's an actual issue on the server side, the server usually responds with a 5xx error which indicates an issue on the server. So the client has done everything right but the server itself has issues. 3xx codes are usually redirect or caching responses while 2xx indicates success.

    A list with all response codes can be found here or here.

    Note that if this is a public endpoint, there may be a limit how fast / how often you can send a request to the server. If you're too fast, the server may also responds with a 400. Though usually the server should respond with a 429 Too Many Requests in such cases, Though 400 is just a generic issue with the request.
     
    Kurt-Dekker likes this.
  15. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,732
    Is this occurring in Editor or some particular platform?
    Try printing out downloadHandler.text, it may contain more exact info about what server did not like.
    One think to pay attention to is query. Did it reach server?
     
  16. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    This is occurring in the Editor. downloadHandler.text is empty.
    Yes, it does reach the server. The way I was able to debug this was I installed an IActionFilter with Mvc Services (if that makes any sense? I'm using AspNetCore) and was able to set breakpoints in OnActionExecuted(). But here's the thing. The message does get processed by my MVC controller event handler, but ONLY if I have the breakpoint set. If not, it does not get processed.
    So, I realize that this probably means something is up with my server.

    I can easily call GET on the same url, using a button event in Unity, and it succeeds. Call it in Postman, and it succeeds, but somehow when calling as a response to the SignalR event, it doesn't succeed. I've added a number of thread handling techniques and delays as suggested in the thread, but even with a delay, I get the 400 error. I was looking for a place to capture where I'm actually setting the status code on the server, but still trying to find that.

    Thanks everyone for all your help so far. I realize this kind of goes beyond the scope of the actual UnityWebRequest call, as that seems to work. It's just how the server is handling it, which I'm still digging into.

    EDIT:
    I'm also publishing my Unity project to WebGL, and the resulting Javascript does NOT have this problem. It works as expected. It's only when in the Editor that I get this. I haven't tried a standalone windows build though.
     
  17. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,990
    But the HTTP protocol is a quite simple text based protocol. So if you actually send the same request it should cause the same result. So something has to be different.

    Your best bet would be to install something like charlesproxy on your system. That way you should be angle to inspect both, the request and the response. Do this from your standalone build and once from WebGL where it works and compare the two requests.
     
    Kurt-Dekker likes this.
  18. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,732
    For such cases you need to install Fiddler/Charles proxy or similar and examine the raw HTTP request. There is some difference between the one that works and the one that doesn't. This is the most straight-forward way to figure out the problem.
     
  19. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    An update on this, I just used Charles proxy. In the Editor, if I specify ip as "localhost.charlesproxy.com", it works one hundred percent of the time, and the GET request is produced as expected. Once I switch back to "localhost", I start getting the 400 error again. Also going to reiterate that making the GET request by itself succeeds. It's only when the GET request is made after handling the SignalR event does the issue happen. I also just tried a standalone desktop build, and the issue is the same.
     
  20. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Try 127.0.0.1 instead of "localhost" Compare the Charles Proxy capture when running from curl (working) vs the Editor (not working). Feel free to send me the .chls files in a private message and I'll take a look.
     
  21. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,513
    Hmm, not sure I understand. When I use Charles proxy, I use "localhost.charlesproxy.com", and everything works as expected. It's when I use "localhost" or "127.0.0.1" that I get the 400 error, in which case, I can't capture anything via Charles.
     
  22. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,732
    Right, Unity does not use proxy for localhost. Try running server on a different machine.
     
    JeffDUnity3D likes this.