내 응용 프로그램에서 클라이언트로부터 괜찮은 크기의 패킷을 보내고 적절한 크기의 응답을 수신하므로 일부 압축을 구현하고 싶을 때가 있습니다.WebApi에서 들어오는 요청의 압축을 푸는 DelegatingHandler
도중에 IIS의 동적 압축을 사용하여 나를 대신 할 수 있기 때문에 괜찮습니다.하지만 다음과 같은 문제가 있습니다. (이 코드의 대부분은 Fabrik.Common의 부품 (https://github.com/benfoster/Fabrik.Common)를 기반으로)
public class DecompressionHandler : DelegatingHandler
{
public Collection<ICompressor> Compressors;
public DecompressionHandler()
{
Compressors = new Collection<ICompressor> {new GZipCompressor(), new DeflateCompressor()};
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (request.Content.Headers.ContentEncoding.IsntNullOrEmpty() && request.Content != null)
{
var encoding = request.Content.Headers.ContentEncoding.First();
var compressor = Compressors.FirstOrDefault(c => c.EncodingType.Equals(encoding, StringComparison.InvariantCultureIgnoreCase));
if (compressor != null)
{
request.Content = await DecompressContentAsync(request.Content, compressor).ConfigureAwait(true);
}
}
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(true);
return response;
}
private static async Task<HttpContent> DecompressContentAsync(HttpContent compressedContent, ICompressor compressor)
{
using (compressedContent)
{
var decompressed = new MemoryStream();
await compressor.Decompress(await compressedContent.ReadAsStreamAsync(), decompressed).ConfigureAwait(true);
// set position back to 0 so it can be read again
decompressed.Position = 0;
var newContent = new StreamContent(decompressed);
// copy content type so we know how to load correct formatter
newContent.Headers.ContentType = compressedContent.Headers.ContentType;
return newContent;
}
}
}
public class DeflateCompressor : Compressor
{
private const string DeflateEncoding = "deflate";
public override string EncodingType
{
get { return DeflateEncoding; }
}
public override Stream CreateCompressionStream(Stream output)
{
return new DeflateStream(output, CompressionMode.Compress, leaveOpen: true);
}
public override Stream CreateDecompressionStream(Stream input)
{
return new DeflateStream(input, CompressionMode.Decompress, leaveOpen: true);
}
}
public abstract class Compressor : ICompressor
{
public abstract string EncodingType { get; }
public abstract Stream CreateCompressionStream(Stream output);
public abstract Stream CreateDecompressionStream(Stream input);
public virtual Task Compress(Stream source, Stream destination)
{
var compressed = CreateCompressionStream(destination);
return Pump(source, compressed)
.ContinueWith(task => compressed.Dispose());
}
public virtual Task Decompress(Stream source, Stream destination)
{
var decompressed = CreateDecompressionStream(source);
return Pump(decompressed, destination)
.ContinueWith(task => decompressed.Dispose());
}
protected virtual Task Pump(Stream input, Stream output)
{
return input.CopyToAsync(output);
}
}
public interface ICompressor
{
string EncodingType { get; }
Task Compress(Stream source, Stream destination);
Task Decompress(Stream source, Stream destination);
}
public class GZipCompressor : Compressor
{
private const string GZipEncoding = "gzip";
public override string EncodingType
{
get { return GZipEncoding; }
}
public override Stream CreateCompressionStream(Stream output)
{
return new GZipStream(output, CompressionMode.Compress, leaveOpen: true);
}
public override Stream CreateDecompressionStream(Stream input)
{
return new GZipStream(input, CompressionMode.Decompress, leaveOpen: true);
}
}
압축 해제가 잘 작동하고 내가 가진 :
나는 들어오는 요청을 압축 해제가 앉아 위임 핸들러가 내 요청. 압축 해제 된 JSON 결과가 컨텐츠에 채워집니다.
내가 base.SendAsync에 전달하고 내 컨트롤러 메서드를 눌렀을 때 모델이 null 인 반면 압축을 구현하기 전에는 모두 훌륭하게 작동했습니다.
콘텐츠 스트림을 읽을 때 한 번 읽는다고 읽었지만 request.content를 압축 해제 된 결과로 설정하면 다시 읽어야합니다.