Skip to content

Xamarin IWebServiceRequest interface

wvdvegt edited this page Apr 5, 2018 · 10 revisions

This interface basically is supplied with all parts/data necessary to make a rest request.

  • Note: It has a ASYNC symbol that if defined will enable async/await code necessary for portable assemblies.
  • Note: the latest RequestSetttings contains a hasBinaryResponse property to switch between the text of the body being returned or the raw binary body content as a stream.
  • Note: Some headers are mapped on properties and are therefor not allowed in the headers collection.

Portable/.Net version:

    #region IWebServiceRequest Members

    #if ASYNC
    public void WebServiceRequest(RequestSetttings requestSettings, out RequestResponse requestReponse)
    {
        // Wrap the actual method in a Task. Neccesary because we cannot:
        // 1) Make this method async (out is not allowed) 
        // 2) Return a Task<RequestResponse> as it breaks the interface (only void does not break it).
        //
        Task<RequestResponse> taskName = Task.Factory.StartNew<RequestResponse>(() =>
        {
            return WebServiceRequestAsync(requestSettings).Result;
        });

        requestReponse = taskName.Result;
    }

    /// <summary>
    /// Web service request.
    /// </summary>
    ///
    /// <param name="requestSettings"> Options for controlling the operation. </param>
    ///
    /// <returns>
    /// A RequestResponse.
    /// </returns>
    private async Task<RequestResponse> WebServiceRequestAsync(RequestSetttings requestSettings)
    #else
    /// <summary>
    /// Web service request.
    /// </summary>
    ///
    /// <param name="requestSettings">  Options for controlling the operation. </param>
    /// <param name="requestResponse"> The request response. </param>
    public void WebServiceRequest(RequestSetttings requestSettings, out RequestResponse requestResponse)
    {
        requestResponse = WebServiceRequest(requestSettings);
    }

    /// <summary>
    /// Web service request.
    /// </summary>
    ///
    /// <param name="requestSettings">  Options for controlling the operation. </param>
    ///
    /// <returns>
    /// A RequestResponse.
    /// </returns>
    private RequestResponse WebServiceRequest(RequestSetttings requestSettings)
    #endif
    {
        RequestResponse result = new RequestResponse(requestSettings);

        try
        {
            //! Might throw a silent System.IOException on .NET 3.5 (sync).
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestSettings.uri);

            request.Method = requestSettings.method;

            // Both Accept and Content-Type are not allowed as Headers in a HttpWebRequest.
            // They need to be assigned to a matching property.

            if (requestSettings.requestHeaders.ContainsKey("Accept"))
            {
                request.Accept = requestSettings.requestHeaders["Accept"];
            }

            if (!String.IsNullOrEmpty(requestSettings.body))
            {
                byte[] data = Encoding.UTF8.GetBytes(requestSettings.body);

                if (requestSettings.requestHeaders.ContainsKey("Content-Type"))
                {
                    request.ContentType = requestSettings.requestHeaders["Content-Type"];
                }

                foreach (KeyValuePair<string, string> kvp in requestSettings.requestHeaders)
                {
                    if (kvp.Key.Equals("Accept") || kvp.Key.Equals("Content-Type"))
                    {
                        continue;
                    }
                    request.Headers.Add(kvp.Key, kvp.Value);
                }

                request.ContentLength = data.Length;

                // See https://msdn.microsoft.com/en-us/library/system.net.servicepoint.expect100continue(v=vs.110).aspx
                // A2 currently does not support this 100-Continue response for POST requets.
                request.ServicePoint.Expect100Continue = false;

    #if ASYNC
                Stream stream = await request.GetRequestStreamAsync();
                await stream.WriteAsync(data, 0, data.Length);
                stream.Close();
    #else
                Stream stream = request.GetRequestStream();
                stream.Write(data, 0, data.Length);
                stream.Close();
    #endif
            }
            else
            {
                foreach (KeyValuePair<string, string> kvp in requestSettings.requestHeaders)
                {
                    if (kvp.Key.Equals("Accept") || kvp.Key.Equals("Content-Type"))
                    {
                        continue;
                    }
                    request.Headers.Add(kvp.Key, kvp.Value);
                }
            }

    #if ASYNC
            WebResponse response = await request.GetResponseAsync();
    #else
            WebResponse response = request.GetResponse();
    #endif
            if (response.Headers.HasKeys())
            {
                foreach (string key in response.Headers.AllKeys)
                {
                    result.responseHeaders.Add(key, response.Headers.Get(key));
                }
            }

            result.responseCode = (int)(response as HttpWebResponse).StatusCode;

            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
    #if ASYNC
                if (result.hasBinaryResponse)
                {
                    result.binaryResponse = await StreamToByteArrayAsync(reader.BaseStream);
                }
                else
                {
                    result.body = await reader.ReadToEndAsync();
                }
    #else
                if (result.hasBinaryResponse)
                {
                    result.binaryResponse = StreamToByteArray(reader.BaseStream);
                }
                else
                {
                    result.body = reader.ReadToEnd();
                }
    #endif
            }
        }
        catch (Exception e)
        {
            result.responsMessage = e.Message;

            Log(Severity.Error, String.Format("{0} - {1}", e.GetType().Name, e.Message));
        }

        return result;
    }

    #if ASYNC
    /// <summary>
    /// Stream to byte array asynchronous.
    /// </summary>
    ///
    /// <param name="inputStream">  Stream to read data from. </param>
    ///
    /// <returns>
    /// A byte[].
    /// </returns>
    private async Task<byte[]> StreamToByteArrayAsync(Stream inputStream)
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            await inputStream.CopyToAsync(memStream);

            return memStream.ToArray();
        }
    }
    #else
    /// <summary>
    /// Stream to byte array.
    /// </summary>
    ///
    /// <param name="inputStream">  Stream to read data from. </param>
    ///
    /// <returns>
    /// A byte[].
    /// </returns>
    private byte[] StreamToByteArray(Stream inputStream)
    {
        byte[] bytes = new byte[4069];

        using (MemoryStream memoryStream = new MemoryStream())
        {
            int count;

            while ((count = inputStream.Read(bytes, 0, bytes.Length)) > 0)
            {
                memoryStream.Write(bytes, 0, count);
            }

            return memoryStream.ToArray();
        }
    }
    #endif

    #endregion IWebServiceRequest Members

Unity version using UnityWebRequest

  • Note: The Unity version requires the bridge to have a property and constructor like:

      #region
      private MonoBehaviour behaviour {
      	get;
      	set;
      }
      public Bridge(MonoBehaviour behaviour) : 
      	this() {
      	this.behaviour = behaviour;
      	// If we do not derive from MonoBehaviour Start is never called.
      	Start();
      }
      #endregion
    

#region IWebServiceRequest implementation

// http://docs.unity3d.com/ScriptReference/Experimental.Networking.UnityWebRequest.html
//
// Note: We use a synchronous way to invoke a 'Coroutine'. 
//
// The idea is that the Asset Method is invoked using a Coroutine and not these methodes 
// themselves that are invoked by the asset. The reason is that inside the asset we need 
// the answers of this method (like the Auth Token, or be sure the GameStorage structure has 
// been restored before restoring data etc).
//
// Unity3D differ a lot from Xamarin in this (async) matter.
//
// The WWW class is not used as UCM used PUT and DElETE als http methods (WWW only supports GET and POST).
//
public void WebServiceRequest (RequestSetttings requestSettings, out RequestResponse requestResponse)
{
	requestResponse = new RequestResponse(requestSettings);

	//Coroutine cr = behaviour.StartCoroutine(
	InternalWebServiceRequest(requestSettings, requestResponse);

	Debug.Log(String.Format("{0} - {1}",  requestResponse.responseCode, requestResponse.uri));
}

// http://answers.unity3d.com/questions/1028197/best-practise-to-switch-between-coroutine-and-sync.html
// http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know
//
// 'Fake' Coroutine.
private Coroutine InternalWebServiceRequest(RequestSetttings requestSettings, RequestResponse requestResponse)
{
	UnityWebRequest request = new UnityWebRequest();

	Debug.Log(requestSettings.uri);

	request.method = requestSettings.method;
	request.url = requestSettings.uri.ToString();

	foreach (KeyValuePair<String,String> kvp in requestSettings.requestHeaders) {
		request.SetRequestHeader(kvp.Key, kvp.Value);
	}

	if (!String.IsNullOrEmpty(requestSettings.body)) 
	{
		byte[] data = Encoding.UTF8.GetBytes(requestSettings.body);
    UploadHandlerRaw upHandler = new UploadHandlerRaw(data);
		upHandler.contentType = requestSettings.requestHeaders["Content-Type"];
		request.uploadHandler = upHandler;
}

	DownloadHandler dnHandler = new DownloadHandlerBuffer();
	request.downloadHandler = dnHandler;
	request.Send();

	while (!request.isDone) {
		// Nothing
	}

	if (request.isError) {
		Debug.Log(request.error);
		requestResponse.responsMessage = request.error;
	} else {
		requestResponse.responseCode = (int)request.responseCode;
		requestResponse.responseHeaders=request.GetResponseHeaders();
		requestResponse.body=request.downloadHandler.text;

		// Unity does not have a responseMessage available.
		requestResponse.responsMessage = String.Empty;
	}

	return null;
}

#endregion

Clone this wiki locally