I have a fairly bog standard .net MVC 4 Web API application.
public class LogsController : ApiController
{
public HttpResponseMessage PostLog(List<LogDto> logs)
{
if (logs != null && logs.Any())
{
var goodLogs = new List<Log>();
var badLogs = new List<LogBad>();
foreach (var logDto in logs)
{
if (logDto.IsValid())
{
goodLogs.Add(logDto.ToLog());
}
else
{
badLogs.Add(logDto.ToLogBad());
}
}
if (goodLogs.Any())
{
_logsRepo.Save(goodLogs);
}
if(badLogs.Any())
{
_logsBadRepo.Save(badLogs);
}
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
This all work fine, I have devices that are able to send me their logs and it works well. However now we are starting to have concerns about the size of the data being transferred, and we want to have a look at accepting post that have been compressed using GZIP?
How would I go about do this? Is it setting in IIS or could I user Action Filters?
EDIT 1
Following up from Filip's answer my thinking is that I need to intercept the processing of the request before it gets to my controller. If i can catch the request before the Web api framework attempts to parse the body of the request into my business object, which fails because the body of the request is still compressed. Then I can decompress the body of the request and then pass the request back into the processing chain, and hopefully the Web Api framework will be able to parse the (decompressed) body into my business objects.
It looks Like using the DelagatingHandler is the way to go. It allows me access to the request during the processing, but before my controller. So I tried the following?
public class gZipHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
string encodingType = request.Headers.AcceptEncoding.First().Value;
request.Content = new DeCompressedContent(request.Content, encodingType);
return base.SendAsync(request, cancellationToken);
}
}
public class DeCompressedContent : HttpContent
{
private HttpContent originalContent;
private string encodingType;
public DeCompressedContent(HttpContent content, string encodType)
{
originalContent = content;
encodingType = encodType;
}
protected override bool TryComputeLength(out long length)
{
length = -1;
return false;
}
protected override Task<Stream> CreateContentReadStreamAsync()
{
return base.CreateContentReadStreamAsync();
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
Stream compressedStream = null;
if (encodingType == "gzip")
{
compressedStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true);
}
return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
{
if (compressedStream != null)
{
compressedStream.Dispose();
}
});
}
}
}
This seems to be working ok. The SendAsync method is being called before my controller and the constructor for the DecompressedContent is being called. However the SerializeToStreamAsync is never being called so I added the CreateContentReadStreamAsync to see if that's where the decompressing should be happening, but that's not being called either.
I fell like I am close to the solution, but just need a little bit extra to get it over the line.
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…