I'm using WWW to interact with a RESTful web service. I have a problem sending XML files to the server through POST requests, though. This is my code:
if(Syste.IO.File.Exists(filePath)){
byte [] raw = File.ReadAllBytes(filePath);
WWWForm form = new WWWForm();
form.AddBinaryData("fileUpload", raw, "", "text/xml");
WWW www = new WWW(host + auth + "/behaviors", form);
StartCoroutine(myCoroutine(www));
}
IEnumerator myCoroutine(WWW www){
yield return www;
if (www.error == null)
{
Debug.Log("Text: " + www.text);
proceedToNextRequest = true;
} else {
Debug.Log("Error: "+ www.error);
Application.Quit();
}
}
The answer from the server is, though, "Unsupported Media Type", and I've no idea what's wrong. I generally use POSTMAN on Google Chrome to send these requests, and it works fine. Any tips?
I have found a solution for this: instead of using the WWW class(which, anyway, according to the documentation I'm pretty sure it can be used for this pupose), I'm using WebRequest. How this can be achieved is very well explained in the previous link and in this question: HTTP post XML data in C#
Related
I'm a beginner in Unity3D; i have to develop a mobile app and i need to manage user profile data; i have to communicate these data with server using REST services.
Everything works fine when i send Json (eg name, email, phone number, etc.) from my app, but I can't update the profile picture.
What i need is:
Content-Type = multipart/form-data
key="profile_picture", value=file_to_upload (not the path)
I read a lot about networking in Unity and tried different combinations of UnityWebRequest, List, WWWform but nothing seems to work for this kind of PUT service.
UnityWebRequest www = new UnityWebRequest(URL + user.email, "PUT");
www.SetRequestHeader("Content-Type", "multipart/form-data");
www.SetRequestHeader("AUTHORIZATION", authorization);
//i think here i'm missing the correct way to set up the content
I can correctly simulate the update from Postman, so it's not a problem with server; i'm pretty sure that the problem is that i can't convert this logic inside the app.
Upload from Postman correctly working(1)
Upload from Postman correctly working(2)
Any kind of help and code suggestion will be appreciated.
Thanks
With Put you usually only send file data but without a form.
You can add a multipart form using UnityWebRequest.Post
IEnumerator Upload()
{
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormFileSection("profile_picture", byte[], "example.png", "image/png"));
UnityWebRequest www = UnityWebRequest.Post(url, formData);
// change the method name
www.method = "PUT";
yield return www.SendWebRequest();
if(www.error)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
}
}
using a MultipartFormFileSection
Or alternatively you can use a WWWForm
IEnumerator Upload()
{
WWWForm form = new WWWForm();
form.AddBinaryData("profile_picture", bytes, "filename.png", "image/png");
// Upload via post request
var www = UnityWebRequest.Post(screenShotURL, form);
// change the method name
www.method = "PUT";
yield return www.SendWebRequest();
if (www.error)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Finished Uploading Screenshot");
}
}
using WWWForm.AddBinaryData
Note that for user authentication you have to encode your credentials properly:
string authenticate(string username, string password)
{
string auth = username + ":" + password;
auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
auth = "Basic " + auth;
return auth;
}
www.SetRequestHeader("AUTHORIZATION", authenticate("user", "password"));
(Source)
When Unity sends the POST request it doesn't pass the POST data, so the server returns an error (it gets the server response). I've seen that several people had a similar issue and it got fixed by adding www.chunkedTransfer = false;, however, that doesn't work for me.
I've also seen that some people use WWWForm instead of IMultipartFormSection, but I haven't tried it because it is deprecated.
I'm using PHP, but I've also tried it with Perl and it didn't work either. When I manually send a POST request everything works normally, so it seems the issue is in Unity. I'm new to Unity, so any help would be appreciated. I'm using the current latest version, 2018.2.18f1 Personal.
My code is pretty much the same as the official Unity documentation for sending POST request, but apparently it doesn't work. Here is my code:
C#:
public void Click() {
StartCoroutine(PostRequest("http://127.0.0.1/test.php", "help"));
}
IEnumerator PostRequest(string url, string data) {
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormDataSection("data=" + data));
UnityWebRequest www = UnityWebRequest.Post(url, formData);
www.chunkedTransfer = false;
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
} else {
Debug.Log(www.downloadHandler.text);
}
}
PHP:
<?php echo "Server received: " . $_POST["data"]; ?>
Christoph Lütjen pointed out that according to this it should be new MultipartFormDataSection("data", data), despite the official documentation example using new MultipartFormDataSection("field1=foo&field2=bar").
Changing it to new MultipartFormDataSection("data", data) fixed the issue.
I'm trying to get data from a remote server using C#. I tried using a simple script to see if "it works", but I'm not getting a response. The same script works on a local server, so I'd like to know how I can do it.
This is my C# code:
IEnumerator Sayhi()
{
WWWForm sendInfo = new WWWForm();
sendInfo.AddField("function", function);
WWW www = new WWW(bl_LoginDataBase.Instance.GetUrl(bl_LoginDataBase.URLType.CallFunction), sendInfo);
yield return www;
Debug.Log(www.text);
}
And the PHP code:
<?php
echo "Hi";
I expected the Debug.Log(www.text); to print Hi, which it does if I use a local machine (http://192.168.0.whatever), but the remote server (http://whatever.example.com) doesn't return anything. I tried making the php fail so it returns an error, make a database and return some values from there, but nothing. I'd like to point out it does work on a local server, and works as intended.
What am I doing wrong?
Edit: By the way, if I access http://www.whatever.example.com/Function.php via browser, it shows the echo result.
C# has built in classes and methods to help you perform such tasks.You can use the WebClient class to connect to web servers (using GET or POST) and even send form values to it easily. See below:
string url ="http://www.whatever.example.com/Function.php";
var reqparam = new System.Collections.Specialized.NameValueCollection();
reqparam.Add("name", "John Doe");
try
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
byte[] responsebytes = client.UploadValues(url, "POST", parameters);
string output = System.Text.Encoding.UTF8.GetString(responsebytes);
}
}
catch (Exception x)
{
//an error occured
}
The variable output will contain the response you want "Hi".
I am working on a project that requires to oauth2 token to communicate. The backend gives me a curl command, but I have no idea how to put it into WWW form in Unity because I have no former experience with http or json file. Could you help me to access the token? Thanks. Here how the curl code looks like:
$ curl -v -u {CLIENT_ID}:{CLIENT_SECRET} "https://api.domo.com/oauth/token?grant_type=client_credentials&scope={SCOPE}"
and here is an example:
$ curl -v -u 441e307a-b2a1-4a99-8561-174e5b153fsa:f103fc453d08bdh049edc9a1913e3f5266447a06d1d2751258c89771fbcc8087 "https://api.domo.com/oauth/token?grant_type=client_credentials&scope=data%20user"
Thank you so much!
To get oauth2 token with the WWW API, use the WWWForm to create form that contains the grant_type, client_id and client_secret. When you make the request, the token should be returned in Json format. Use Unity's JsonUtility to convert it to string you can easily obtain.
Retrieving Token:
[Serializable]
public class TokenClassName
{
public string access_token;
}
IEnumerator GetAccessToken()
{
var url = "http://yoururl.com";
var form = new WWWForm();
form.AddField("grant_type", "client_credentials");
form.AddField("client_id", "login-secret");
form.AddField("client_secret", "secretpassword");
WWW www = new WWW(url, form);
//wait for request to complete
yield return www;
//and check for errors
if (String.IsNullOrEmpty(www.error))
{
string resultContent = www.text;
UnityEngine.Debug.Log(resultContent);
TokenClassName json = JsonUtility.FromJson<TokenClassName>(resultContent);
UnityEngine.Debug.Log("Token: " + json.access_token);
}
else
{
//something wrong!
UnityEngine.Debug.Log("WWW Error: " + www.error);
}
}
Using the Token:
After you receive the token, it will be stored in the json.access_token variable. You can use then use that to make requests by adding it to the Header of your other WWW requests. That header is stored in a Dictionary like this:
headers.Add("Authorization", "Bearer " + token);
Here is an full example for making a request with the received token:
IEnumerator makeRequestWithToken(string token)
{
var url = "http://yoururl.com";
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Authorization", "Bearer " + token);
WWWForm formData = new WWWForm();
formData.AddField("YourField", "YourVal");
formData.AddField("YourField", "YourVal");
WWW www = new WWW(url, formData.data, headers);
yield return www;
//and check for errors
if (String.IsNullOrEmpty(www.error))
{
string resultContent = www.text;
UnityEngine.Debug.Log(resultContent);
}
else
{
//something wrong!
UnityEngine.Debug.Log("WWW Error: " + www.error);
}
}
Unity now uses UnityWebRequest. If possible start using that. Here is an example of retrieving oauth2 with UnityWebRequest.
Note that both functions are coroutine functions and must be started with the StartCoroutine function like StartCoroutine(GetAccessToken()); and StartCoroutine(makeRequestWithToken("YourToken"));.
I got it working using the following method:
Refer to Travis's fitbit code
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_clientID + ":" + _consumerSecret);
var encoded = Convert.ToBase64String(plainTextBytes);
var form = new WWWForm();
form.AddField("client_id", _clientID);
form.AddField("grant_type", "authorization_code");
form.AddField("redirect_uri", WWW.UnEscapeURL(CallBackUrl));
form.AddField("code", _returnCode);
var headers = form.headers;
headers["Authorization"] = "Basic " + encoded;
_wwwRequest = new WWW("https://api.fitbit.com/oauth2/token", form.data, headers);
Relate to your backend, see what kind of oauth2 they are using, you can see the oauth2 documentation Here. Feel free to leave a comment for question.
I'm trying to send an image to my NodeJS API from Unity but unfortunately I can't make it work. Here is an extract of my c# code:
private string POSTUrl = "http://myNodeJSAPI.com/upload-image";
private Dictionary<string, string> postHeader = new Dictionary<string, string>();
public WWW POST() {
WWW www;
postHeader["content-Type"] = "application/octet-stream";
postData = File.ReadAllBytes("Assets/Resources/ImageIWantToSend.png");
www = new WWW(POSTUrl, postData, postHeader);
StartCoroutine(WaitForRequest(www));
return www;
}
IEnumerator WaitForRequest(WWW www)
{
yield return www;
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.text);
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
and here the relevant part of my nodejs one:
app.post('/upload-image', rawBody, function (req, res) {
console.log("File received!");
if (req.rawBody && req.bodyLength > 0) {
fs.writeFile("/tmp/ImageIWantToSend.png", req.rawBody, function (err) {
if (err) {
return console.log(err);
}
console.log("Image has been saved! Starting other things...");
})
}
}
The request is sent but never received by the API (I get 503 bad gateway). I try the same request outside Unity and it seems to work (using Postman: basic post request with image attached as binary).
Did I make a mistake, or is there another way to achieve this?
Thanks!
EDIT: I finally solved the problem by changing application/octet-stream to text/html. If there is a better way to send data to Nodejs I'm still interested
Check out Unity's new UnityWebRequest class.It is a replacement for original WWW.
You can also use C# WebClient or HttpWebRequest class. They work with Unity if you are not using WebGL build.
This is also a good unity plugin to handle requests, though it is not free.