ASP.NET Core Web API how to log requests and responses into database

Cenk 11 Reputation points
2022-11-30T05:29:36.42+00:00

Hi guys,

I have this ASP.NET web API and I am upgrading it to Core. In my legacy application, I log requests and responses into an SQL database. I wonder if there is a way to do it in Core?

Here is the way I log;

public class RequestResponseHandler : DelegatingHandler  
    {  
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,  
            CancellationToken cancellationToken)  
        {  
            var requestedMethod = request.Method;  
            var userHostAddress = HttpContext.Current != null ? HttpContext.Current.Request.UserHostAddress : "0.0.0.0";  
            var useragent = request.Headers.UserAgent.ToString();  
            var requestMessage = await request.Content.ReadAsByteArrayAsync();  
            var uriAccessed = request.RequestUri.AbsoluteUri;  
  
            var responseHeadersString = new StringBuilder();  
            foreach (var header in request.Headers)  
            {  
                responseHeadersString.Append($"{header.Key}: {String.Join(", ", header.Value)}{Environment.NewLine}");  
            }  
  
            var messageLoggingHandler = new MessageLogging();  
  
            var requestLog = new ApiLog()  
            {  
                Headers = responseHeadersString.ToString(),  
                AbsoluteUri = uriAccessed,  
                Host = userHostAddress,  
                RequestBody = Encoding.UTF8.GetString(requestMessage),  
                UserHostAddress = userHostAddress,  
                Useragent = useragent,  
                RequestedMethod = requestedMethod.ToString(),  
                StatusCode = string.Empty  
            };  
  
            messageLoggingHandler.IncomingMessageAsync(requestLog);  
  
            var response = await base.SendAsync(request, cancellationToken);  
  
            byte[] responseMessage = new byte[] { 0x1 };  
            if (response.IsSuccessStatusCode)  
            {  
                if (response.Content != null)  
                {  
                    responseMessage = await response.Content.ReadAsByteArrayAsync();  
                }  
            }  
            else  
                responseMessage = Encoding.UTF8.GetBytes(response.ReasonPhrase);  
  
            var responseLog = new ApiLog()  
            {  
                Headers = responseHeadersString.ToString(),  
                AbsoluteUri = uriAccessed,  
                Host = userHostAddress,  
                RequestBody = Encoding.UTF8.GetString(responseMessage),  
                UserHostAddress = userHostAddress,  
                Useragent = useragent,  
                RequestedMethod = requestedMethod.ToString(),  
                StatusCode = response.StatusCode.ToString()  
            };  
  
            messageLoggingHandler.OutgoingMessageAsync(responseLog);  
            return response;  
        }  
    }  

public class MessageLogging  
    {  
        public void IncomingMessageAsync(ApiLog apiLog)  
        {  
            apiLog.RequestType = "Request";  
            var sqlErrorLogging = new ApiLogging();  
            sqlErrorLogging.InsertLog(apiLog);  
        }  
  
        public void OutgoingMessageAsync(ApiLog apiLog)  
        {  
            apiLog.RequestType = "Response";  
            var sqlErrorLogging = new ApiLogging();  
            sqlErrorLogging.InsertLog(apiLog);  
        }  
    }  

public class ApiLogging  
    {  
        public void InsertLog(ApiLog apiLog)  
        {  
            try  
            {  
                using (var sqlConnection =  
                    new SqlConnection(ConfigurationManager.ConnectionStrings["GameContext"].ConnectionString))  
                {  
                    using (SqlCommand cmd = new SqlCommand(  
                        "INSERT INTO [dbo].[API_Log] ([Host],[Headers],[StatusCode],[TimeUtc],[RequestBody],[RequestedMethod],[UserHostAddress],[Useragent],[AbsoluteUri],[RequestType])	VALUES (@Host,@Headers,@StatusCode,getdate(),@RequestBody,@RequestedMethod,@UserHostAddress,@Useragent,@AbsoluteUri,@RequestType)",  
                        sqlConnection))  
                    {  
                        sqlConnection.Open();  
                        cmd.Parameters.AddWithValue("@Host", apiLog.Host);  
                        cmd.Parameters.AddWithValue("@Headers", apiLog.Headers);  
                        cmd.Parameters.AddWithValue("@StatusCode", apiLog.StatusCode);  
                        cmd.Parameters.AddWithValue("@RequestBody", apiLog.RequestBody);  
                        cmd.Parameters.AddWithValue("@RequestedMethod", apiLog.RequestedMethod);  
                        cmd.Parameters.AddWithValue("@UserHostAddress", apiLog.UserHostAddress);  
                        cmd.Parameters.AddWithValue("@Useragent", apiLog.Useragent);  
                        cmd.Parameters.AddWithValue("@AbsoluteUri", apiLog.AbsoluteUri);  
                        cmd.Parameters.AddWithValue("@RequestType", apiLog.RequestType);  
                        cmd.ExecuteNonQuery();  
                    }  
                }  
            }  
            catch (Exception e)  
            {  
                Console.WriteLine(e);  
                throw;  
            }  
        }  
    }  
ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,197 questions
0 comments No comments
{count} vote

1 answer

Sort by: Most helpful
  1. Zhi Lv - MSFT 32,016 Reputation points Microsoft Vendor
    2022-11-30T10:07:37.757+00:00

    Hi @Cenk ,

    I have this ASP.NET web API and I am upgrading it to Core. In my legacy application, I log requests and responses into an SQL database. I wonder if there is a way to do it in Core?

    In asp.net core, you can create a custom middleware to capture both HTTP requests and responses.

    Code like this:

    public class RequestResponseLoggerMiddleware  
    {  
        private readonly RequestDelegate _next;  
        private readonly bool _isRequestResponseLoggingEnabled;  
      
        public RequestResponseLoggerMiddleware(RequestDelegate next, IConfiguration config)  
        {  
            _next = next;  
            _isRequestResponseLoggingEnabled = config.GetValue<bool>("EnableRequestResponseLogging", false);  
        }  
      
        public async Task InvokeAsync(HttpContext httpContext)  
        {  
            // Middleware is enabled only when the EnableRequestResponseLogging config value is set.  
            if (_isRequestResponseLoggingEnabled)  
            {  
                Console.WriteLine($"HTTP request information:\n" +  
                    $"\tMethod: {httpContext.Request.Method}\n" +  
                    $"\tPath: {httpContext.Request.Path}\n" +  
                    $"\tQueryString: {httpContext.Request.QueryString}\n" +  
                    $"\tHeaders: {FormatHeaders(httpContext.Request.Headers)}\n" +  
                    $"\tSchema: {httpContext.Request.Scheme}\n" +  
                    $"\tHost: {httpContext.Request.Host}\n" +  
                    $"\tBody: {await ReadBodyFromRequest(httpContext.Request)}");  
      
                // Temporarily replace the HttpResponseStream, which is a write-only stream, with a MemoryStream to capture it's value in-flight.  
                var originalResponseBody = httpContext.Response.Body;  
                using var newResponseBody = new MemoryStream();  
                httpContext.Response.Body = newResponseBody;  
      
                // Call the next middleware in the pipeline  
                await _next(httpContext);  
      
                newResponseBody.Seek(0, SeekOrigin.Begin);  
                var responseBodyText = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();  
      
                Console.WriteLine($"HTTP request information:\n" +  
                    $"\tStatusCode: {httpContext.Response.StatusCode}\n" +  
                    $"\tContentType: {httpContext.Response.ContentType}\n" +  
                    $"\tHeaders: {FormatHeaders(httpContext.Response.Headers)}\n" +  
                    $"\tBody: {responseBodyText}");  
      
                newResponseBody.Seek(0, SeekOrigin.Begin);  
                await newResponseBody.CopyToAsync(originalResponseBody);  
            }  
            else  
            {  
                await _next(httpContext);  
            }  
        }  
      
        private static string FormatHeaders(IHeaderDictionary headers) => string.Join(", ", headers.Select(kvp => $"{<!-- -->{<!-- -->{kvp.Key}: {string.Join(", ", kvp.Value)}}}"));  
      
        private static async Task<string> ReadBodyFromRequest(HttpRequest request)  
        {  
            // Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).  
            request.EnableBuffering();  
      
            using var streamReader = new StreamReader(request.Body, leaveOpen: true);  
            var requestBody = await streamReader.ReadToEndAsync();  
      
            // Reset the request's body stream position for next middleware in the pipeline.  
            request.Body.Position = 0;  
            return requestBody;  
        }  
    }  
    

    Then, call the custom middleware using app.UseMiddleware<RequestResponseLoggerMiddleware>(); or create a Custom middleware extension.

    More detail code, you can refer to this sample.

    Besides, there also has another tutorial, you can check it: Request Response Logging Middleware ASP.NET Core

    Then, to insert the request or response data into database, you can inject the DBContext in the Custom Middleware InvokeAsync method, and then insert the data into the database. Code like this:

    public async Task InvokeAsync(HttpContext context, ApplicationDbContext dbContext)  
    {  
    

    You can refer to this article: Factory-based middleware activation in ASP.NET Core


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Dillion

    1 person found this answer helpful.