Monday, January 8, 2018

How to log file contents in request body of a multipart/form-data request

Leave a Comment

I am trying to log all requests in my asp.net web API project to a text file. I am using DelegationHandler feature to implement logging mechanism in my application, below is the code snippet for that,

public class MyAPILogHandler : DelegatingHandler     {         protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)         {            // Captures all properties from the request.             var apiLogEntry = CreateApiLogEntryWithRequestData(request);             if (request.Content != null)             {                     await request.Content.ReadAsStringAsync()                         .ContinueWith(task =>                         {                             apiLogEntry.RequestContentBody = task.Result;                         }, cancellationToken);             }              return await base.SendAsync(request, cancellationToken)                 .ContinueWith(task =>                 {                     var response = task.Result;                      // Update the API log entry with response info                     apiLogEntry.ResponseStatusCode = (int)response.StatusCode;                     apiLogEntry.ResponseTimestamp = DateTime.Now;                      if (response.Content != null)                     {                         apiLogEntry.ResponseContentBody = response.Content.ReadAsStringAsync().Result;                         apiLogEntry.ResponseContentType = response.Content.Headers.ContentType.MediaType;                         apiLogEntry.ResponseHeaders = SerializeHeaders(response.Content.Headers);                     }                         var logger = new LogManager();                     logger.Log(new LogMessage()                     {                        Message = PrepareLogMessage(apiLogEntry),                        LogTo = LogSource.File                     });                      return response;                 }, cancellationToken);         } } 

Above implementation is working as expected and it is logging all required request/response information to the file.

But when we make any multipart/form-data POST api call with some images attached, after logging this request, log file becomes huge, because all image/binary content is getting converted into string and writing down it the text file. please find below log file content,

Body:  ----------------------------079603462429865781513947 Content-Disposition: form-data; name="batchid"  22649EEE-3994-4225-AF73-D9A6B659CAE3 ----------------------------079603462429865781513947 Content-Disposition: form-data; name="files"; filename="d.png" Content-Type: image/png  PNG   IHDR    í   %v ¸   sRGB ®Îé   gAMA  ±üa      pHYs  à  ÃÇo¨d  ÿ¥IDATx^ìýX]K¶( ·îsß»ß÷þï{O÷iÛ    Á2âîîîÁe¹âîî,<@   Á$÷w_ÈZó5$Dwvv×} ----------------------------4334344396037865656556781513947 Content-Disposition: form-data; name="files"; filename="m.png" Content-Type: image/png  PNG   IHDR    í   %v ¸   sRGB ®Îé   gAMA  ±üa      pHYs  à  ÃÇo¨d  ÿ¥IDATx^ìýX]K¶( ·îsß»ß÷þï{O÷iÛ    Á2âîîîÁe¹âîî,<@   Á$÷w_ÈZó5$Dwvv×} 

I don't want to log the binary content of a request body, it could be sufficient to log only request body file contents like,

    ----------------------------079603462429865781513947     Content-Disposition: form-data; name="batchid"      22649EEE-3994-4225-AF73-D9A6B659CAE3     ----------------------------079603462429865781513947     Content-Disposition: form-data; name="files"; filename="d.png"     Content-Type: image/png      ----------------------------4334344396037865656556781513947     Content-Disposition: form-data; name="files"; filename="m.png"     Content-Type: image/png 

Can you please suggest how to prevent logging binary content of request body and log only file contents of a request body.

2 Answers

Answers 1

From what I gather, you are implementing something similar to this approach. When uploading a file (i.e., a request type "multipart/form-data") its actual content always begins with the "Content-Type: {ContentTypeValue}\r\n\r\n" sequence and the next header begins with "\r\n--" sequence (as it is illustrated in your logs). You can explore more info about raw request data parsing at ReferenceSource. So, you can strip everything about file (if exists), for example, via RegEx:

Content-Type: {ContentTypeOrOctetStream}\r\n\r\n{FileContentBytesToRemove}\r\n

using System.Text.RegularExpressions; ... string StripRawFileContentIfExists(string input) {     if(input.IndexOf("Content-Type") == -1)         return input;     string regExPattern = "(?<ContentTypeGroup>Content-Type: .*?\\r\\n\\r\\n)(?<FileRawContentGroup>.*?)(?<NextHeaderBeginGroup>\\r\\n--)";     return Regex.Replace(input, regExPattern, me => me.Groups["ContentTypeGroup"].Value + string.Empty + me.Groups["NextHeaderBeginGroup"].Value); } ... //apiLogEntry.RequestContentBody = task.Result; apiLogEntry.RequestContentBody = StripRawFileContentIfExists(task.Result); 

Answers 2

I'd suggest separate huge content from log. like you encountered, it just screws everything up in the log. to some extent disables log functionality.

I'd suggest you organize those huge content in file system. like this:

---request-a/   |--request-a-body-multi-part1.txt   |--request-a-body-multi-part2.txt 

and just maintain a link in your log to reference this file system path.

hope it helps.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment