Skip to content

Commit

Permalink
add conneg
Browse files Browse the repository at this point in the history
  • Loading branch information
lvermeulen committed Apr 15, 2017
1 parent f617967 commit 96a8412
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 133 deletions.
11 changes: 6 additions & 5 deletions src/Flurl.Http.Xml/CapturedXmlContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ namespace Flurl.Http.Xml
/// </summary>
public class CapturedXmlContent : CapturedStringContent
{
/// <summary>
/// Initializes a new instance of the <see cref="CapturedXmlContent"/> class.
/// </summary>
/// <param name="xml">The XML.</param>
public CapturedXmlContent(string xml) : base(xml, Encoding.UTF8, "application/xml") { }
/// <summary>
/// Initializes a new instance of the <see cref="CapturedXmlContent"/> class.
/// </summary>
/// <param name="xml">The XML.</param>
/// <param name="mediaType">The media-type.</param>
public CapturedXmlContent(string xml, string mediaType) : base(xml, Encoding.UTF8, mediaType) { }
}
}
112 changes: 66 additions & 46 deletions src/Flurl.Http.Xml/FlurlClientExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -12,47 +14,49 @@ namespace Flurl.Http.Xml
/// </summary>
public static class FlurlClientExtensions
{
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// A Task whose result is the XML response body deserialized to an object of type T.
/// </returns>
public static Task<T> GetXmlAsync<T>(this FlurlClient client, CancellationToken cancellationToken)
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// A Task whose result is the XML response body deserialized to an object of type T.
/// </returns>
public static Task<T> GetXmlAsync<T>(this IFlurlClient client, CancellationToken cancellationToken)
{
return client.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken).ReceiveXml<T>();
}

/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <returns>A Task whose result is the XML response body deserialized to an object of type T.</returns>
public static Task<T> GetXmlAsync<T>(this FlurlClient client)
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <param name="client">The client.</param>
/// <returns>A Task whose result is the XML response body deserialized to an object of type T.</returns>
public static Task<T> GetXmlAsync<T>(this IFlurlClient client)
{
return client.SendAsync(HttpMethod.Get).ReceiveXml<T>();
}

/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// A Task whose result is the XML response body parsed into an XDocument.
/// </returns>
public static Task<XDocument> GetXDocumentAsync(this FlurlClient client, CancellationToken cancellationToken)
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// A Task whose result is the XML response body parsed into an XDocument.
/// </returns>
public static Task<XDocument> GetXDocumentAsync(this IFlurlClient client, CancellationToken cancellationToken)
{
return client.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken).ReceiveXDocument();
}

/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <returns>A Task whose result is the XML response body parsed into an XDocument.</returns>
public static Task<XDocument> GetXDocumentAsync(this FlurlClient client)
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <param name="client">The client.</param>
/// <returns>A Task whose result is the XML response body parsed into an XDocument.</returns>
public static Task<XDocument> GetXDocumentAsync(this IFlurlClient client)
{
return client.SendAsync(HttpMethod.Get).ReceiveXDocument();
}
Expand All @@ -75,7 +79,7 @@ public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this FlurlClient
/// Sends an asynchronous GET request.
/// </summary>
/// <returns>A Task whose result is the XML response body parsed into a collection of XElements.</returns>
public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this FlurlClient client, string expression)
public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this IFlurlClient client, string expression)
{
return client.SendAsync(HttpMethod.Get).ReceiveXElementsFromXPath(expression);
}
Expand All @@ -90,7 +94,7 @@ public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this FlurlClient
/// <returns>
/// A Task whose result is the XML response body parsed into a collection of XElements.
/// </returns>
public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this FlurlClient client, string expression, IXmlNamespaceResolver resolver, CancellationToken cancellationToken)
public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this IFlurlClient client, string expression, IXmlNamespaceResolver resolver, CancellationToken cancellationToken)
{
return client.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken).ReceiveXElementsFromXPath(expression, resolver);
}
Expand All @@ -99,23 +103,39 @@ public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this FlurlClient
/// Sends an asynchronous GET request.
/// </summary>
/// <returns>A Task whose result is the XML response body parsed into a collection of XElements.</returns>
public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this FlurlClient client, string expression, IXmlNamespaceResolver resolver)
public static Task<IEnumerable<XElement>> GetXElementsFromXPath(this IFlurlClient client, string expression, IXmlNamespaceResolver resolver)
{
return client.SendAsync(HttpMethod.Get).ReceiveXElementsFromXPath(expression, resolver);
}

/// <summary>
/// Sends an asynchronous POST request.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="data">Contents of the request body.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// A Task whose result is the received HttpResponseMessage.
/// </returns>
public static Task<HttpResponseMessage> PostXmlAsync(this FlurlClient client, object data, CancellationToken cancellationToken)
private static string GetMediaType(this IFlurlClient client)
{
if (client.HttpClient.DefaultRequestHeaders.Accept.Any())
{
// return media type of first accepted media type containing "xml", else of first accepted media type
var requestHeaders = client.HttpClient.DefaultRequestHeaders;
var accept = requestHeaders.Accept.First(x => x.MediaType.IndexOf("xml", StringComparison.OrdinalIgnoreCase) >= 0)
?? requestHeaders.Accept.First();

return accept.MediaType;
}

// no accepted media type present, return default
return "application/xml";
}

/// <summary>
/// Sends an asynchronous POST request.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="data">Contents of the request body.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// A Task whose result is the received HttpResponseMessage.
/// </returns>
public static Task<HttpResponseMessage> PostXmlAsync(this IFlurlClient client, object data, CancellationToken cancellationToken)
{
var content = new CapturedXmlContent(client.Settings.XmlSerializer().Serialize(data));
var content = new CapturedXmlContent(client.Settings.XmlSerializer().Serialize(data), client.GetMediaType());
return client.SendAsync(HttpMethod.Post, content: content, cancellationToken: cancellationToken);
}

Expand All @@ -127,9 +147,9 @@ public static Task<HttpResponseMessage> PostXmlAsync(this FlurlClient client, ob
/// <returns>
/// A Task whose result is the received HttpResponseMessage.
/// </returns>
public static Task<HttpResponseMessage> PostXmlAsync(this FlurlClient client, object data)
public static Task<HttpResponseMessage> PostXmlAsync(this IFlurlClient client, object data)
{
var content = new CapturedXmlContent(client.Settings.XmlSerializer().Serialize(data));
var content = new CapturedXmlContent(client.Settings.XmlSerializer().Serialize(data), client.GetMediaType());
return client.SendAsync(HttpMethod.Post, content: content);
}
}
Expand Down
139 changes: 85 additions & 54 deletions src/Flurl.Http.Xml/HttpResponseMessageExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
Expand All @@ -24,62 +26,91 @@ private static HttpCall GetHttpCall(HttpRequestMessage request)
return null;
}

/// <summary>
/// Deserializes XML-formatted HTTP response body to object of type T. Intended to chain off an async HTTP.
/// </summary>
/// <typeparam name="T">A type whose structure matches the expected XML response.</typeparam>
/// <returns>A Task whose result is an object containing data in the response body.</returns>
/// <example>x = await url.PosAsync(data).ReceiveXml&lt;T&gt;()</example>
public static async Task<T> ReceiveXml<T>(this Task<HttpResponseMessage> response)
{
var resp = await response.ConfigureAwait(false);
var call = GetHttpCall(resp.RequestMessage);
try
{
using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
return call.Settings.XmlSerializer().Deserialize<T>(stream);
}
}
catch (Exception ex)
{
var s = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
call.Exception = ex;
throw new FlurlHttpException(call, s, ex);
}
}
private static string GetMediaType(HttpRequestMessage request)
{
if (request.Headers.Accept.Any())
{
// return media type of first accepted media type containing "xml", else of first accepted media type
var acceptHeader = request.Headers.Accept.First(x => x.MediaType.IndexOf("xml", StringComparison.OrdinalIgnoreCase) >= 0)
?? request.Headers.Accept.First();

/// <summary>
/// Parses XML-formatted HTTP response body into an XDocument. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is an XDocument containing XML data from the response body.</returns>
/// <example>d = await url.PostAsync(data).ReceiveXDocument()</example>
public static async Task<XDocument> ReceiveXDocument(this Task<HttpResponseMessage> response)
{
var resp = await response.ConfigureAwait(false);
var call = GetHttpCall(resp.RequestMessage);
try
{
using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var streamReader = new StreamReader(stream))
{
return XDocument.Parse(streamReader.ReadToEnd());
}
}
catch (Exception ex)
{
var s = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
call.Exception = ex;
throw new FlurlHttpException(call, s, ex);
}
}
return acceptHeader.MediaType;
}

/// <summary>
/// Parses XML-formatted HTTP response body into a collection of XElements. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is a collection of XElements from an XDocument containing XML data from the response body.</returns>
/// <example>d = await url.PostAsync(data).ReceiveXElementsFromXPath(xpathExpression)</example>
public static async Task<IEnumerable<XElement>> ReceiveXElementsFromXPath(this Task<HttpResponseMessage> response, string expression)
// no accepted media type present, return default
return "application/xml";
}

/// <summary>
/// Receives XML-formatted HTTP response body. Intended to chain off an async HTTP.
/// </summary>
/// <param name="responseMessage">The response.</param>
/// <returns>A Task whose result is a response message containing data in the response body.</returns>
/// <example>x = await url.PostAsync(data).ReceiveXmlResponseMessage()</example>
public static async Task<HttpResponseMessage> ReceiveXmlResponseMessage(this Task<HttpResponseMessage> responseMessage)
{
var response = await responseMessage.ConfigureAwait(false);
response.Content.Headers.ContentType = new MediaTypeHeaderValue(GetMediaType(response.RequestMessage));

return response;
}

private static async Task<T> ReceiveFromXmlStream<T>(this Task<HttpResponseMessage> response, Func<HttpCall, Stream, T> streamHandler)
{
var resp = await ReceiveXmlResponseMessage(response);
var call = GetHttpCall(resp.RequestMessage);

try
{
using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
return streamHandler(call, stream);
}
}
catch (Exception ex)
{
var s = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
call.Exception = ex;
throw new FlurlHttpException(call, s, ex);
}
}

/// <summary>
/// Deserializes XML-formatted HTTP response body to object of type T. Intended to chain off an async HTTP.
/// </summary>
/// <typeparam name="T">A type whose structure matches the expected XML response.</typeparam>
/// <param name="response">The response.</param>
/// <returns>A Task whose result is an object containing data in the response body.</returns>
/// <example>x = await url.PostAsync(data).ReceiveXml&lt;T&gt;()</example>
public static async Task<T> ReceiveXml<T>(this Task<HttpResponseMessage> response)
{
return await ReceiveFromXmlStream(response, (call, stm) =>
call.Settings.XmlSerializer().Deserialize<T>(stm));
}

/// <summary>
/// Parses XML-formatted HTTP response body into an XDocument. Intended to chain off an async call.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>A Task whose result is an XDocument containing XML data from the response body.</returns>
/// <example>d = await url.PostAsync(data).ReceiveXDocument()</example>
public static async Task<XDocument> ReceiveXDocument(this Task<HttpResponseMessage> response)
{
return await ReceiveFromXmlStream(response, (call, stm) =>
{
using (var streamReader = new StreamReader(stm))
{
return XDocument.Parse(streamReader.ReadToEnd());
}
});
}

/// <summary>
/// Parses XML-formatted HTTP response body into a collection of XElements. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is a collection of XElements from an XDocument containing XML data from the response body.</returns>
/// <example>d = await url.PostAsync(data).ReceiveXElementsFromXPath(xpathExpression)</example>
public static async Task<IEnumerable<XElement>> ReceiveXElementsFromXPath(this Task<HttpResponseMessage> response, string expression)
{
var doc = await response.ReceiveXDocument().ConfigureAwait(false);
return doc.XPathSelectElements(expression);
Expand Down
Loading

0 comments on commit 96a8412

Please sign in to comment.