diff --git a/src/Flurl.Http.Xml/CapturedXmlContent.cs b/src/Flurl.Http.Xml/CapturedXmlContent.cs
index 8fea848..9a485db 100644
--- a/src/Flurl.Http.Xml/CapturedXmlContent.cs
+++ b/src/Flurl.Http.Xml/CapturedXmlContent.cs
@@ -9,10 +9,11 @@ namespace Flurl.Http.Xml
///
public class CapturedXmlContent : CapturedStringContent
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The XML.
- public CapturedXmlContent(string xml) : base(xml, Encoding.UTF8, "application/xml") { }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The XML.
+ /// The media-type.
+ public CapturedXmlContent(string xml, string mediaType) : base(xml, Encoding.UTF8, mediaType) { }
}
}
\ No newline at end of file
diff --git a/src/Flurl.Http.Xml/FlurlClientExtensions.cs b/src/Flurl.Http.Xml/FlurlClientExtensions.cs
index 1eb1a55..be087c5 100644
--- a/src/Flurl.Http.Xml/FlurlClientExtensions.cs
+++ b/src/Flurl.Http.Xml/FlurlClientExtensions.cs
@@ -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;
@@ -12,47 +14,49 @@ namespace Flurl.Http.Xml
///
public static class FlurlClientExtensions
{
- ///
- /// Sends an asynchronous GET request.
- ///
- ///
- /// The client.
- /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
- ///
- /// A Task whose result is the XML response body deserialized to an object of type T.
- ///
- public static Task GetXmlAsync(this FlurlClient client, CancellationToken cancellationToken)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ ///
+ /// The client.
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ ///
+ /// A Task whose result is the XML response body deserialized to an object of type T.
+ ///
+ public static Task GetXmlAsync(this IFlurlClient client, CancellationToken cancellationToken)
{
return client.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken).ReceiveXml();
}
- ///
- /// Sends an asynchronous GET request.
- ///
- /// A Task whose result is the XML response body deserialized to an object of type T.
- public static Task GetXmlAsync(this FlurlClient client)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ /// The client.
+ /// A Task whose result is the XML response body deserialized to an object of type T.
+ public static Task GetXmlAsync(this IFlurlClient client)
{
return client.SendAsync(HttpMethod.Get).ReceiveXml();
}
- ///
- /// Sends an asynchronous GET request.
- ///
- /// The client.
- /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
- ///
- /// A Task whose result is the XML response body parsed into an XDocument.
- ///
- public static Task GetXDocumentAsync(this FlurlClient client, CancellationToken cancellationToken)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ /// The client.
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ ///
+ /// A Task whose result is the XML response body parsed into an XDocument.
+ ///
+ public static Task GetXDocumentAsync(this IFlurlClient client, CancellationToken cancellationToken)
{
return client.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken).ReceiveXDocument();
}
- ///
- /// Sends an asynchronous GET request.
- ///
- /// A Task whose result is the XML response body parsed into an XDocument.
- public static Task GetXDocumentAsync(this FlurlClient client)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ /// The client.
+ /// A Task whose result is the XML response body parsed into an XDocument.
+ public static Task GetXDocumentAsync(this IFlurlClient client)
{
return client.SendAsync(HttpMethod.Get).ReceiveXDocument();
}
@@ -75,7 +79,7 @@ public static Task> GetXElementsFromXPath(this FlurlClient
/// Sends an asynchronous GET request.
///
/// A Task whose result is the XML response body parsed into a collection of XElements.
- public static Task> GetXElementsFromXPath(this FlurlClient client, string expression)
+ public static Task> GetXElementsFromXPath(this IFlurlClient client, string expression)
{
return client.SendAsync(HttpMethod.Get).ReceiveXElementsFromXPath(expression);
}
@@ -90,7 +94,7 @@ public static Task> GetXElementsFromXPath(this FlurlClient
///
/// A Task whose result is the XML response body parsed into a collection of XElements.
///
- public static Task> GetXElementsFromXPath(this FlurlClient client, string expression, IXmlNamespaceResolver resolver, CancellationToken cancellationToken)
+ public static Task> GetXElementsFromXPath(this IFlurlClient client, string expression, IXmlNamespaceResolver resolver, CancellationToken cancellationToken)
{
return client.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken).ReceiveXElementsFromXPath(expression, resolver);
}
@@ -99,23 +103,39 @@ public static Task> GetXElementsFromXPath(this FlurlClient
/// Sends an asynchronous GET request.
///
/// A Task whose result is the XML response body parsed into a collection of XElements.
- public static Task> GetXElementsFromXPath(this FlurlClient client, string expression, IXmlNamespaceResolver resolver)
+ public static Task> GetXElementsFromXPath(this IFlurlClient client, string expression, IXmlNamespaceResolver resolver)
{
return client.SendAsync(HttpMethod.Get).ReceiveXElementsFromXPath(expression, resolver);
}
- ///
- /// Sends an asynchronous POST request.
- ///
- /// The client.
- /// Contents of the request body.
- /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
- ///
- /// A Task whose result is the received HttpResponseMessage.
- ///
- public static Task 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";
+ }
+
+ ///
+ /// Sends an asynchronous POST request.
+ ///
+ /// The client.
+ /// Contents of the request body.
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ ///
+ /// A Task whose result is the received HttpResponseMessage.
+ ///
+ public static Task 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);
}
@@ -127,9 +147,9 @@ public static Task PostXmlAsync(this FlurlClient client, ob
///
/// A Task whose result is the received HttpResponseMessage.
///
- public static Task PostXmlAsync(this FlurlClient client, object data)
+ public static Task 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);
}
}
diff --git a/src/Flurl.Http.Xml/HttpResponseMessageExtensions.cs b/src/Flurl.Http.Xml/HttpResponseMessageExtensions.cs
index c617573..c6bf8d0 100644
--- a/src/Flurl.Http.Xml/HttpResponseMessageExtensions.cs
+++ b/src/Flurl.Http.Xml/HttpResponseMessageExtensions.cs
@@ -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;
@@ -24,62 +26,91 @@ private static HttpCall GetHttpCall(HttpRequestMessage request)
return null;
}
- ///
- /// Deserializes XML-formatted HTTP response body to object of type T. Intended to chain off an async HTTP.
- ///
- /// A type whose structure matches the expected XML response.
- /// A Task whose result is an object containing data in the response body.
- /// x = await url.PosAsync(data).ReceiveXml<T>()
- public static async Task ReceiveXml(this Task 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(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();
- ///
- /// Parses XML-formatted HTTP response body into an XDocument. Intended to chain off an async call.
- ///
- /// A Task whose result is an XDocument containing XML data from the response body.
- /// d = await url.PostAsync(data).ReceiveXDocument()
- public static async Task ReceiveXDocument(this Task 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;
+ }
- ///
- /// Parses XML-formatted HTTP response body into a collection of XElements. Intended to chain off an async call.
- ///
- /// A Task whose result is a collection of XElements from an XDocument containing XML data from the response body.
- /// d = await url.PostAsync(data).ReceiveXElementsFromXPath(xpathExpression)
- public static async Task> ReceiveXElementsFromXPath(this Task response, string expression)
+ // no accepted media type present, return default
+ return "application/xml";
+ }
+
+ ///
+ /// Receives XML-formatted HTTP response body. Intended to chain off an async HTTP.
+ ///
+ /// The response.
+ /// A Task whose result is a response message containing data in the response body.
+ /// x = await url.PostAsync(data).ReceiveXmlResponseMessage()
+ public static async Task ReceiveXmlResponseMessage(this Task responseMessage)
+ {
+ var response = await responseMessage.ConfigureAwait(false);
+ response.Content.Headers.ContentType = new MediaTypeHeaderValue(GetMediaType(response.RequestMessage));
+
+ return response;
+ }
+
+ private static async Task ReceiveFromXmlStream(this Task response, Func 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);
+ }
+ }
+
+ ///
+ /// Deserializes XML-formatted HTTP response body to object of type T. Intended to chain off an async HTTP.
+ ///
+ /// A type whose structure matches the expected XML response.
+ /// The response.
+ /// A Task whose result is an object containing data in the response body.
+ /// x = await url.PostAsync(data).ReceiveXml<T>()
+ public static async Task ReceiveXml(this Task response)
+ {
+ return await ReceiveFromXmlStream(response, (call, stm) =>
+ call.Settings.XmlSerializer().Deserialize(stm));
+ }
+
+ ///
+ /// Parses XML-formatted HTTP response body into an XDocument. Intended to chain off an async call.
+ ///
+ /// The response.
+ /// A Task whose result is an XDocument containing XML data from the response body.
+ /// d = await url.PostAsync(data).ReceiveXDocument()
+ public static async Task ReceiveXDocument(this Task response)
+ {
+ return await ReceiveFromXmlStream(response, (call, stm) =>
+ {
+ using (var streamReader = new StreamReader(stm))
+ {
+ return XDocument.Parse(streamReader.ReadToEnd());
+ }
+ });
+ }
+
+ ///
+ /// Parses XML-formatted HTTP response body into a collection of XElements. Intended to chain off an async call.
+ ///
+ /// A Task whose result is a collection of XElements from an XDocument containing XML data from the response body.
+ /// d = await url.PostAsync(data).ReceiveXElementsFromXPath(xpathExpression)
+ public static async Task> ReceiveXElementsFromXPath(this Task response, string expression)
{
var doc = await response.ReceiveXDocument().ConfigureAwait(false);
return doc.XPathSelectElements(expression);
diff --git a/src/Flurl.Http.Xml/UrlExtensions.cs b/src/Flurl.Http.Xml/UrlExtensions.cs
index f84b7e5..1d71db7 100644
--- a/src/Flurl.Http.Xml/UrlExtensions.cs
+++ b/src/Flurl.Http.Xml/UrlExtensions.cs
@@ -12,39 +12,41 @@ namespace Flurl.Http.Xml
///
public static class UrlExtensions
{
- ///
- /// Sends an asynchronous GET request.
- ///
- ///
- /// The URL.
- /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
- ///
- /// A Task whose result is the XML response body deserialized to an object of type T.
- ///
- public static Task GetXmlAsync(this Url url, CancellationToken cancellationToken)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ ///
+ /// The URL.
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ ///
+ /// A Task whose result is the XML response body deserialized to an object of type T.
+ ///
+ public static Task GetXmlAsync(this Url url, CancellationToken cancellationToken)
=> new FlurlClient(url, true).GetXmlAsync(cancellationToken);
- ///
- /// Sends an asynchronous GET request.
- ///
- /// A Task whose result is the XML response body deserialized to an object of type T.
- public static Task GetXmlAsync(this Url url)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ /// The URL.
+ /// A Task whose result is the XML response body deserialized to an object of type T.
+ public static Task GetXmlAsync(this Url url)
=> new FlurlClient(url, true).GetXmlAsync();
- ///
- /// Sends an asynchronous GET request.
- ///
- /// The URL.
- /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
- /// A Task whose result is the XML response body parsed into an XDocument.
- public static Task GetXDocumentAsync(this Url url, CancellationToken cancellationToken)
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ /// The URL.
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// A Task whose result is the XML response body parsed into an XDocument.
+ public static Task GetXDocumentAsync(this Url url, CancellationToken cancellationToken)
=> new FlurlClient(url, true).GetXDocumentAsync(cancellationToken);
- ///
- /// Sends an asynchronous GET request.
- ///
- /// A Task whose result is the XML response body parsed into an XDocument.
- public static Task GetXDocumentAsync(this Url url) => new FlurlClient(url, true).GetXDocumentAsync();
+ ///
+ /// Sends an asynchronous GET request.
+ ///
+ /// The URL.
+ /// A Task whose result is the XML response body parsed into an XDocument.
+ public static Task GetXDocumentAsync(this Url url) => new FlurlClient(url, true).GetXDocumentAsync();
///
/// Sends an asynchronous GET request.
diff --git a/test/Flurl.Http.Xml.Tests/StringExtensionsShould.cs b/test/Flurl.Http.Xml.Tests/StringExtensionsShould.cs
index 820551d..6dc9958 100644
--- a/test/Flurl.Http.Xml.Tests/StringExtensionsShould.cs
+++ b/test/Flurl.Http.Xml.Tests/StringExtensionsShould.cs
@@ -3,7 +3,6 @@
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
-using Flurl.Http.Configuration;
using Flurl.Http.Xml.Tests.Factories;
using Flurl.Http.Xml.Tests.Models;
using Xunit;
diff --git a/test/Flurl.Http.Xml.Tests/UrlExtensionsShould.cs b/test/Flurl.Http.Xml.Tests/UrlExtensionsShould.cs
index 80a50a2..f14092e 100644
--- a/test/Flurl.Http.Xml.Tests/UrlExtensionsShould.cs
+++ b/test/Flurl.Http.Xml.Tests/UrlExtensionsShould.cs
@@ -166,5 +166,21 @@ public async Task PostXmlToXDocumentWithCancellationTokenAsync()
AssertXDocument(result, 3, "Test");
}
+
+ [Theory]
+ [InlineData("text/xml", "text/xml")]
+ [InlineData("text/something+xml", "text/something+xml")]
+ [InlineData(null, "application/xml")]
+ public async Task ReceiveCorrectMediaType(string acceptMediaType, string expectedContentType)
+ {
+ FlurlHttp.Configure(c => c.HttpClientFactory = new EchoHttpClientFactory());
+
+ var result = await new Url("https://some.url")
+ .WithHeader("Accept", acceptMediaType)
+ .PostXmlAsync(new TestModel { Number = 3, Text = "Test" })
+ .ReceiveXmlResponseMessage();
+
+ Assert.Equal(expectedContentType, result?.Content?.Headers?.ContentType?.MediaType);
+ }
}
}