Created
July 2, 2012 18:19
-
-
Save HenrikFrystykNielsen/3034715 to your computer and use it in GitHub Desktop.
Sample showing a DelegatingHandler which plugs in a special HttpContent wrapper for saving response content to local disk and perform asynchronous post-processing on that file. This allows content from a response to be post-processed, for example to send
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Net; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
namespace ResponseEntityProcessor.Handlers | |
{ | |
/// <summary> | |
/// Wraps an inner <see cref="HttpContent"/> and forces the content to be written | |
/// both to a local file as well as to the output stream when SerializeToStreamAsync | |
/// is called. | |
/// </summary> | |
internal class HttpContentProcessor : HttpContentWrapper | |
{ | |
private const int BufferSize = 8 * 1024; | |
private string _outputPath; | |
private Func<string, Task> _contentProcessor; | |
public HttpContentProcessor(HttpContent innerContent, string outputPath, Func<string, Task> contentProcessor) | |
: base(innerContent) | |
{ | |
if (outputPath == null) | |
{ | |
throw new ArgumentNullException("outputPath"); | |
} | |
if (contentProcessor == null) | |
{ | |
throw new ArgumentNullException("contentProcessor"); | |
} | |
_outputPath = outputPath; | |
_contentProcessor = contentProcessor; | |
} | |
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) | |
{ | |
// First write out content to local file asynchronously | |
string fileName = Guid.NewGuid().ToString("D"); | |
string filePath = Path.Combine(_outputPath, fileName); | |
using (FileStream fStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize, useAsync: true)) | |
{ | |
await InnerContent.CopyToAsync(fStream); | |
} | |
// Now write out content to actual output stream | |
await InnerContent.CopyToAsync(stream); | |
// Invoke content processor so that it can process the content asynchonously | |
try | |
{ | |
await _contentProcessor(filePath); | |
} | |
finally | |
{ | |
// We are done with the local file and can delete it. | |
File.Delete(filePath); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Net; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
namespace ResponseEntityProcessor.Handlers | |
{ | |
/// <summary> | |
/// Helper class for wrapping an inner <see cref="HttpContent"/> for the purpose of processing the data. | |
/// </summary> | |
public abstract class HttpContentWrapper : HttpContent | |
{ | |
protected HttpContentWrapper(HttpContent innerContent) | |
{ | |
if (innerContent == null) | |
{ | |
throw new ArgumentNullException("innertContent"); | |
} | |
InnerContent = innerContent; | |
// Copy headers from inner to outer content | |
foreach (KeyValuePair<string, IEnumerable<string>> header in InnerContent.Headers) | |
{ | |
Headers.TryAddWithoutValidation(header.Key, header.Value); | |
} | |
} | |
protected HttpContent InnerContent { get; private set; } | |
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) | |
{ | |
return InnerContent.CopyToAsync(stream); | |
} | |
protected override bool TryComputeLength(out long length) | |
{ | |
long? contentLength = InnerContent.Headers.ContentLength; | |
if (contentLength.HasValue) | |
{ | |
length = contentLength.Value; | |
return true; | |
} | |
length = -1; | |
return false; | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
base.Dispose(disposing); | |
if (disposing) | |
{ | |
InnerContent.Dispose(); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Net.Http; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace ResponseEntityProcessor.Handlers | |
{ | |
/// <summary> | |
/// This <see cref="DelegatingHandler"/> checks that there is a response entity | |
/// and if so plugs in a <see cref="HttpContentProcessor"/> instance which | |
/// copies the response entity to a local file and then enables post-processing | |
/// on that file. | |
/// </summary> | |
public class ResponseEntityHandler : DelegatingHandler | |
{ | |
private string _outputPath; | |
public ResponseEntityHandler(string outputPath) | |
{ | |
if (outputPath == null) | |
{ | |
throw new ArgumentNullException("outputPath"); | |
} | |
_outputPath = outputPath; | |
} | |
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |
{ | |
// Wait for the response | |
HttpResponseMessage response = await base.SendAsync(request, cancellationToken); | |
// Check if there is any content then plug in our content processor wrapper | |
if (response.Content != null) | |
{ | |
// By plugging in a HttpContentProcessor we can pass a Func<string, Task> | |
// which gets the file name of the local file and then processes it asynchronously. | |
response.Content = new HttpContentProcessor(response.Content, _outputPath, | |
(fileName) => | |
{ | |
// For this sample we only check the local file and then return. | |
// However, here you can do any work you like asynchronously. | |
FileInfo fileInfo = new FileInfo(fileName); | |
System.Console.WriteLine("Local file info: Name: {0}, Size: {1}", fileInfo.Name, fileInfo.Length); | |
return Task.FromResult(true); | |
}); | |
} | |
return response; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment