-
Notifications
You must be signed in to change notification settings - Fork 7
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.
#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
-
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