using System;
using System.Collections.Specialized;
using System.IO;
using System.Threading.Tasks;
using EmbedIO.Utilities;
using Swan;
namespace EmbedIO
{
partial class HttpContextExtensions
{
private static readonly object FormDataKey = new object();
private static readonly object QueryDataKey = new object();
///
/// Asynchronously retrieves the request body as an array of s.
///
/// The on which this method is called.
/// A Task, representing the ongoing operation,
/// whose result will be an array of s containing the request body.
/// is .
public static async Task GetRequestBodyAsByteArrayAsync(this IHttpContext @this)
{
using var buffer = new MemoryStream();
using var stream = @this.OpenRequestStream();
await stream.CopyToAsync(buffer, WebServer.StreamCopyBufferSize, @this.CancellationToken).ConfigureAwait(false);
return buffer.ToArray();
}
///
/// Asynchronously buffers the request body into a read-only .
///
/// The on which this method is called.
/// A Task, representing the ongoing operation,
/// whose result will be a read-only containing the request body.
/// is .
public static async Task GetRequestBodyAsMemoryStreamAsync(this IHttpContext @this)
=> new MemoryStream(
await GetRequestBodyAsByteArrayAsync(@this).ConfigureAwait(false),
false);
///
/// Asynchronously retrieves the request body as a string.
///
/// The on which this method is called.
/// A Task, representing the ongoing operation,
/// whose result will be a representation of the request body.
/// is .
public static async Task GetRequestBodyAsStringAsync(this IHttpContext @this)
{
using var reader = @this.OpenRequestText();
return await reader.ReadToEndAsync().ConfigureAwait(false);
}
///
/// Asynchronously deserializes a request body, using the default request deserializer.
/// As of EmbedIO version 3.0, the default response serializer has the same behavior of JSON
/// request parsing methods of version 2.
///
/// The expected type of the deserialized data.
/// The on which this method is called.
/// A Task, representing the ongoing operation,
/// whose result will be the deserialized data.
/// is .
public static Task GetRequestDataAsync(this IHttpContext @this)
=> RequestDeserializer.Default(@this);
///
/// Asynchronously deserializes a request body, using the specified request deserializer.
///
/// The expected type of the deserialized data.
/// The on which this method is called.
/// A used to deserialize the request body.
/// A Task, representing the ongoing operation,
/// whose result will be the deserialized data.
/// is .
/// is .
public static Task GetRequestDataAsync(this IHttpContext @this,RequestDeserializerCallback deserializer)
=> Validate.NotNull(nameof(deserializer), deserializer)(@this);
///
/// Asynchronously parses a request body in application/x-www-form-urlencoded format.
///
/// The on which this method is called.
/// A Task, representing the ongoing operation,
/// whose result will be a read-only of form field names and values.
/// is .
///
/// This method may safely be called more than once for the same :
/// it will return the same collection instead of trying to parse the request body again.
///
public static async Task GetRequestFormDataAsync(this IHttpContext @this)
{
if (!@this.Items.TryGetValue(FormDataKey, out var previousResult))
{
NameValueCollection result;
try
{
using var reader = @this.OpenRequestText();
result = UrlEncodedDataParser.Parse(await reader.ReadToEndAsync().ConfigureAwait(false), false);
}
catch (Exception e)
{
@this.Items[FormDataKey] = e;
throw;
}
@this.Items[FormDataKey] = result;
return result;
}
switch (previousResult)
{
case NameValueCollection collection:
return collection;
case Exception exception:
throw exception.RethrowPreservingStackTrace();
case null:
throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestFormDataAsync)} is null.");
default:
throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestFormDataAsync)} is of unexpected type {previousResult.GetType().FullName}");
}
}
///
/// Parses a request URL query. Note that this is different from getting the property,
/// in that fields without an equal sign are treated as if they have an empty value, instead of their keys being grouped
/// as values of the null key.
///
/// The on which this method is called.
/// A read-only .
/// is .
///
/// This method may safely be called more than once for the same :
/// it will return the same collection instead of trying to parse the request body again.
///
public static NameValueCollection GetRequestQueryData(this IHttpContext @this)
{
if (!@this.Items.TryGetValue(QueryDataKey, out var previousResult))
{
NameValueCollection result;
try
{
result = UrlEncodedDataParser.Parse(@this.Request.Url.Query, false);
}
catch (Exception e)
{
@this.Items[FormDataKey] = e;
throw;
}
@this.Items[FormDataKey] = result;
return result;
}
switch (previousResult)
{
case NameValueCollection collection:
return collection;
case Exception exception:
throw exception.RethrowPreservingStackTrace();
case null:
throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestQueryData)} is null.");
default:
throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestQueryData)} is of unexpected type {previousResult.GetType().FullName}");
}
}
}
}