C# Web API Core 3.0将HttpRequest主体记录为自定义类对象
我正在使用自定义中间件拦截HTTP请求,并希望使用NLog记录对.json文件的请求和响应。 当我想将请求/响应正文从字符串反序列化为自定义类对象时,就会出现问题,然后POST/PUT/DELETE方法不起作用 Postman on POST方法引发:500内部服务器错误 Angular app on POST方法引发:从源“”访问“myValidEndpoint”处的XMLHttpRequest已被CORS策略阻止:请求的资源上不存在“Access Control Allow origin”标头 如果我将请求/响应主体记录为字符串,那么每个http方法都可以正常工作。 代码如下: Program.csC# Web API Core 3.0将HttpRequest主体记录为自定义类对象,c#,asp.net-core,asp.net-core-webapi,nlog,C#,Asp.net Core,Asp.net Core Webapi,Nlog,我正在使用自定义中间件拦截HTTP请求,并希望使用NLog记录对.json文件的请求和响应。 当我想将请求/响应正文从字符串反序列化为自定义类对象时,就会出现问题,然后POST/PUT/DELETE方法不起作用 Postman on POST方法引发:500内部服务器错误 Angular app on POST方法引发:从源“”访问“myValidEndpoint”处的XMLHttpRequest已被CORS策略阻止:请求的资源上不存在“Access Control Allow origin”标
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public Startup(IConfiguration configuration)
{
LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public HttpConfiguration Config { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDrzavaService, DrzavaService>();
services.AddTransient<IGradService, GradService>();
services.AddEntityFrameworkNpgsql().AddDbContext<DrzavedbContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DrzaveConnection")))
.AddUnitOfWork<DrzavedbContext>();
services.AddControllers().AddNewtonsoftJson();
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("http://localhost:4200").AllowAnyHeader().AllowAnyMethod();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMyMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<ExceptionMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly Logger _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
Stopwatch _stopwatch;
public RequestLoggingMiddleware(RequestDelegate next)
{
_next = next;
_logger = LogManager.GetCurrentClassLogger();
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
}
public async Task Invoke(HttpContext context)
{
await LogRequest(context);
await LogResponse(context);
}
private async Task LogRequest(HttpContext context)
{
_stopwatch = Stopwatch.StartNew();
context.Request.Headers.Add("X-Request-Guid", Guid.NewGuid().ToString());
context.Request.EnableBuffering();
await using var requestStream = _recyclableMemoryStreamManager.GetStream();
await context.Request.Body.CopyToAsync(requestStream);
string bodyString = ReadStreamInChunks(requestStream);
List<BodyItem> body = JsonConvert.DeserializeObject<List<BodyItem>>(bodyString);
RequestModel requestModel = new RequestModel()
{
requestStart = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"),
method = context.Request.Method,
schema = context.Request.Scheme,
host = context.Request.Host.ToString(),
path = context.Request.Path,
requestBody = body,
requestGuid = context.Request.Headers["X-Request-Guid"]
};
_logger.Info("{request}", requestModel);
context.Request.Body.Position = 0;
}
private static string ReadStreamInChunks(Stream stream)
{
const int readChunkBufferLength = 4096;
stream.Seek(0, SeekOrigin.Begin);
using var textWriter = new StringWriter();
using var reader = new StreamReader(stream);
var readChunk = new char[readChunkBufferLength];
int readChunkLength;
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
return textWriter.ToString();
}
private async Task LogResponse(HttpContext context)
{
context.Response.Headers.Add("X-Request-Guid", context.Request.Headers["X-Request-Guid"].ToString());
var originalBodyStream = context.Response.Body;
await using var responseBody = _recyclableMemoryStreamManager.GetStream();
context.Response.Body = responseBody;
await _next(context);
context.Response.Body.Seek(0, SeekOrigin.Begin);
string bodyStream = await new StreamReader(context.Response.Body).ReadToEndAsync();
List<BodyItem> body = JsonConvert.DeserializeObject<List<BodyItem>>(bodyStream);
context.Response.Body.Seek(0, SeekOrigin.Begin);
ResponseModel responseModel = new ResponseModel()
{
requestEnd = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"),
responseBody = body,
responseGuid = context.Response.Headers["X-Request-Guid"],
statusCode = context.Response.StatusCode.ToString(),
requestDuration = _stopwatch.ElapsedMilliseconds
};
_logger.Info("{response}", JsonConvert.SerializeObject(responseModel));
await responseBody.CopyToAsync(originalBodyStream);
}
}
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLoggingMiddleware>();
}
}
[JsonObject]
class BodyItem
{
[JsonProperty]
public int id { get; set; }
[JsonProperty]
public string name { get; set; }
[JsonProperty]
public int? population { get; set; }
[JsonProperty]
public int? countryId{ get; set; }
}
class RequestModel
{
public string requestStart { get; set; }
public string method { get; set; }
public string schema { get; set; }
public string host { get; set; }
public string path { get; set; }
public List<BodyItem> requestBody { get; set; }
public string requestGuid { get; set; }
}
class ResponseModel
{
public string requestEnd { get; set; }
public List<BodyItem> responseBody { get; set; }
public string responseGuid { get; set; }
public string statusCode { get; set; }
public float requestDuration { get; set; }
}
RequestModel.cs
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public Startup(IConfiguration configuration)
{
LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public HttpConfiguration Config { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDrzavaService, DrzavaService>();
services.AddTransient<IGradService, GradService>();
services.AddEntityFrameworkNpgsql().AddDbContext<DrzavedbContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DrzaveConnection")))
.AddUnitOfWork<DrzavedbContext>();
services.AddControllers().AddNewtonsoftJson();
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("http://localhost:4200").AllowAnyHeader().AllowAnyMethod();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMyMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<ExceptionMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly Logger _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
Stopwatch _stopwatch;
public RequestLoggingMiddleware(RequestDelegate next)
{
_next = next;
_logger = LogManager.GetCurrentClassLogger();
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
}
public async Task Invoke(HttpContext context)
{
await LogRequest(context);
await LogResponse(context);
}
private async Task LogRequest(HttpContext context)
{
_stopwatch = Stopwatch.StartNew();
context.Request.Headers.Add("X-Request-Guid", Guid.NewGuid().ToString());
context.Request.EnableBuffering();
await using var requestStream = _recyclableMemoryStreamManager.GetStream();
await context.Request.Body.CopyToAsync(requestStream);
string bodyString = ReadStreamInChunks(requestStream);
List<BodyItem> body = JsonConvert.DeserializeObject<List<BodyItem>>(bodyString);
RequestModel requestModel = new RequestModel()
{
requestStart = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"),
method = context.Request.Method,
schema = context.Request.Scheme,
host = context.Request.Host.ToString(),
path = context.Request.Path,
requestBody = body,
requestGuid = context.Request.Headers["X-Request-Guid"]
};
_logger.Info("{request}", requestModel);
context.Request.Body.Position = 0;
}
private static string ReadStreamInChunks(Stream stream)
{
const int readChunkBufferLength = 4096;
stream.Seek(0, SeekOrigin.Begin);
using var textWriter = new StringWriter();
using var reader = new StreamReader(stream);
var readChunk = new char[readChunkBufferLength];
int readChunkLength;
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
return textWriter.ToString();
}
private async Task LogResponse(HttpContext context)
{
context.Response.Headers.Add("X-Request-Guid", context.Request.Headers["X-Request-Guid"].ToString());
var originalBodyStream = context.Response.Body;
await using var responseBody = _recyclableMemoryStreamManager.GetStream();
context.Response.Body = responseBody;
await _next(context);
context.Response.Body.Seek(0, SeekOrigin.Begin);
string bodyStream = await new StreamReader(context.Response.Body).ReadToEndAsync();
List<BodyItem> body = JsonConvert.DeserializeObject<List<BodyItem>>(bodyStream);
context.Response.Body.Seek(0, SeekOrigin.Begin);
ResponseModel responseModel = new ResponseModel()
{
requestEnd = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"),
responseBody = body,
responseGuid = context.Response.Headers["X-Request-Guid"],
statusCode = context.Response.StatusCode.ToString(),
requestDuration = _stopwatch.ElapsedMilliseconds
};
_logger.Info("{response}", JsonConvert.SerializeObject(responseModel));
await responseBody.CopyToAsync(originalBodyStream);
}
}
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLoggingMiddleware>();
}
}
[JsonObject]
class BodyItem
{
[JsonProperty]
public int id { get; set; }
[JsonProperty]
public string name { get; set; }
[JsonProperty]
public int? population { get; set; }
[JsonProperty]
public int? countryId{ get; set; }
}
class RequestModel
{
public string requestStart { get; set; }
public string method { get; set; }
public string schema { get; set; }
public string host { get; set; }
public string path { get; set; }
public List<BodyItem> requestBody { get; set; }
public string requestGuid { get; set; }
}
class ResponseModel
{
public string requestEnd { get; set; }
public List<BodyItem> responseBody { get; set; }
public string responseGuid { get; set; }
public string statusCode { get; set; }
public float requestDuration { get; set; }
}
类请求模型
{
公共字符串requestStart{get;set;}
公共字符串方法{get;set;}
公共字符串架构{get;set;}
公共字符串主机{get;set;}
公共字符串路径{get;set;}
公共列表请求主体{get;set;}
公共字符串requestGuid{get;set;}
}
ResponseModel.cs
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public Startup(IConfiguration configuration)
{
LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public HttpConfiguration Config { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDrzavaService, DrzavaService>();
services.AddTransient<IGradService, GradService>();
services.AddEntityFrameworkNpgsql().AddDbContext<DrzavedbContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DrzaveConnection")))
.AddUnitOfWork<DrzavedbContext>();
services.AddControllers().AddNewtonsoftJson();
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("http://localhost:4200").AllowAnyHeader().AllowAnyMethod();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMyMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<ExceptionMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly Logger _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
Stopwatch _stopwatch;
public RequestLoggingMiddleware(RequestDelegate next)
{
_next = next;
_logger = LogManager.GetCurrentClassLogger();
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
}
public async Task Invoke(HttpContext context)
{
await LogRequest(context);
await LogResponse(context);
}
private async Task LogRequest(HttpContext context)
{
_stopwatch = Stopwatch.StartNew();
context.Request.Headers.Add("X-Request-Guid", Guid.NewGuid().ToString());
context.Request.EnableBuffering();
await using var requestStream = _recyclableMemoryStreamManager.GetStream();
await context.Request.Body.CopyToAsync(requestStream);
string bodyString = ReadStreamInChunks(requestStream);
List<BodyItem> body = JsonConvert.DeserializeObject<List<BodyItem>>(bodyString);
RequestModel requestModel = new RequestModel()
{
requestStart = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"),
method = context.Request.Method,
schema = context.Request.Scheme,
host = context.Request.Host.ToString(),
path = context.Request.Path,
requestBody = body,
requestGuid = context.Request.Headers["X-Request-Guid"]
};
_logger.Info("{request}", requestModel);
context.Request.Body.Position = 0;
}
private static string ReadStreamInChunks(Stream stream)
{
const int readChunkBufferLength = 4096;
stream.Seek(0, SeekOrigin.Begin);
using var textWriter = new StringWriter();
using var reader = new StreamReader(stream);
var readChunk = new char[readChunkBufferLength];
int readChunkLength;
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
return textWriter.ToString();
}
private async Task LogResponse(HttpContext context)
{
context.Response.Headers.Add("X-Request-Guid", context.Request.Headers["X-Request-Guid"].ToString());
var originalBodyStream = context.Response.Body;
await using var responseBody = _recyclableMemoryStreamManager.GetStream();
context.Response.Body = responseBody;
await _next(context);
context.Response.Body.Seek(0, SeekOrigin.Begin);
string bodyStream = await new StreamReader(context.Response.Body).ReadToEndAsync();
List<BodyItem> body = JsonConvert.DeserializeObject<List<BodyItem>>(bodyStream);
context.Response.Body.Seek(0, SeekOrigin.Begin);
ResponseModel responseModel = new ResponseModel()
{
requestEnd = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"),
responseBody = body,
responseGuid = context.Response.Headers["X-Request-Guid"],
statusCode = context.Response.StatusCode.ToString(),
requestDuration = _stopwatch.ElapsedMilliseconds
};
_logger.Info("{response}", JsonConvert.SerializeObject(responseModel));
await responseBody.CopyToAsync(originalBodyStream);
}
}
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLoggingMiddleware>();
}
}
[JsonObject]
class BodyItem
{
[JsonProperty]
public int id { get; set; }
[JsonProperty]
public string name { get; set; }
[JsonProperty]
public int? population { get; set; }
[JsonProperty]
public int? countryId{ get; set; }
}
class RequestModel
{
public string requestStart { get; set; }
public string method { get; set; }
public string schema { get; set; }
public string host { get; set; }
public string path { get; set; }
public List<BodyItem> requestBody { get; set; }
public string requestGuid { get; set; }
}
class ResponseModel
{
public string requestEnd { get; set; }
public List<BodyItem> responseBody { get; set; }
public string responseGuid { get; set; }
public string statusCode { get; set; }
public float requestDuration { get; set; }
}
类响应模型
{
公共字符串requestEnd{get;set;}
公共列表响应主体{get;set;}
公共字符串responseGuid{get;set;}
公共字符串状态码{get;set;}
公共浮点请求持续时间{get;set;}
}
当我想将请求/响应主体从字符串反序列化为自定义类对象,然后POST/PUT/DELETE方法不起作用时,就会出现问题
对于上述问题,正如我们在评论中所讨论的,API使用者(或响应主体)发送的来自请求主体的数据可能并不总是能够反序列化为List对象,因此它可能会在自定义中间件代码逻辑中导致错误
为了解决这个问题,您可以构建额外的代码逻辑来检查请求路径等,以反序列化到不同的自定义类对象
private async Task LogRequest(HttpContext context)
{
// check context.Request.Path
// or context.Request.Method etc
// perform different code logic to deserialize to different custom class object
if (context.Request.Path.ToString() == "/api/{your_controller_name}/{action_name}")
{
// code logic here
}
}
当我想将请求/响应主体从字符串反序列化为自定义类对象,然后POST/PUT/DELETE方法不起作用时,就会出现问题
对于上述问题,正如我们在评论中所讨论的,API使用者(或响应主体)发送的来自请求主体的数据可能并不总是能够反序列化为List对象,因此它可能会在自定义中间件代码逻辑中导致错误
为了解决这个问题,您可以构建额外的代码逻辑来检查请求路径等,以反序列化到不同的自定义类对象
private async Task LogRequest(HttpContext context)
{
// check context.Request.Path
// or context.Request.Method etc
// perform different code logic to deserialize to different custom class object
if (context.Request.Path.ToString() == "/api/{your_controller_name}/{action_name}")
{
// code logic here
}
}
Postman-on-POST方法抛出:500个内部服务器错误
您是否尝试调试代码并检查哪个代码段导致自定义服务器出现异常/错误Middleware@Fei韩:是的。将请求/响应正文字符串反序列化为自定义类对象List body=JsonConvert.DeserializeObject(bodyString)代码>转换时会出现问题,即使我在代码中不使用此变量并使用字符串表示主体时也是如此。但是如果我对这行进行注释,一切都会正常工作
如果请求正文中的数据无法反序列化到列表
对象,则会导致错误。您确定API使用者发送的所有请求正文都应该反序列化到List
对象吗?也许您需要根据context.Request.Path
执行不同的代码逻辑来反序列化到不同的自定义类对象。谢谢您,先生,我通过您的提示理解了这个问题。当我试图将一个对象反序列化为响应对象列表时,出现了问题。这就是GET response工作的原因,因为它有更多的对象要反序列化,但POST方法只发送一个对象!!现在,我必须检查我的响应主体中有多少个对象,并相应地构建逻辑。Postman on POST方法抛出:500内部服务器错误
您是否尝试调试代码并检查哪个代码段导致自定义服务器中出现异常/错误Middleware@Fei韩:是的。将请求/响应正文字符串反序列化为自定义类对象List body=JsonConvert.DeserializeObject(bodyString)代码>转换时会出现问题,即使我在代码中不使用此变量并使用字符串表示主体时也是如此。但是如果我对这行进行注释,一切都会正常工作
如果请求正文中的数据无法反序列化到列表
对象,则会导致错误。您确定API使用者发送的所有请求正文都应该反序列化到List
对象吗?也许您需要根据context.Request.Path
执行不同的代码逻辑来反序列化到不同的自定义类对象。谢谢您,先生,我通过您的提示理解了这个问题。当我试图将一个对象反序列化为响应对象列表时,出现了问题。这就是GET response工作的原因,因为它有更多的对象要反序列化,但POST方法只发送一个对象!!现在我必须检查我的响应体中有多少个对象,并相应地构建逻辑;如果(bodyString!=string.Empty){body=JsonConvert.DeserializeObject(bodyString);}或者{body=new JObject();}您可以根据您的实际场景和需求构建逻辑。再次感谢您,我是这样做的:对象体;如果(bodyString!=string.Empty){body=JsonConvert.DeserializeObject(bodyString);}或者{body=new JObject();}
您可以根据实际的场景和需求构建逻辑。