我将Asp.Net Core用作Rest Api服务。 我需要访问ActionFilter中的请求和响应。实际上,我在OnActionExcecuted中找到了请求,但无法读取响应结果。
我正在尝试按以下方式返回值:
[HttpGet]
[ProducesResponseType(typeof(ResponseType), (int)HttpStatusCode.OK)]
[Route("[action]")]
public async Task<IActionResult> Get(CancellationToken cancellationToken)
{
var model = await _responseServices.Get(cancellationToken);
return Ok(model);
}
在ActionFilter的OnExcecuted方法中,如下所示:
_request = context.HttpContext.Request.ReadAsString().Result;
_response = context.HttpContext.Response.ReadAsString().Result; //?
我正在尝试在ReadAsString中获取响应,作为扩展方法,如下所示:
public static async Task<string> ReadAsString(this HttpResponse response)
{
var initialBody = response.Body;
var buffer = new byte[Convert.ToInt32(response.ContentLength)];
await response.Body.ReadAsync(buffer, 0, buffer.Length);
var body = Encoding.UTF8.GetString(buffer);
response.Body = initialBody;
return body;
}
但是,没有结果!
如何在OnActionExcecuted中获得响应?
感谢大家花时间尝试并帮助解释
答案 0 :(得分:5)
如果您要记录json结果/查看结果,则无需阅读整个响应流。只需序列化context.Result
:
public class MyFilterAttribute : ActionFilterAttribute
{
private ILogger<MyFilterAttribute> logger;
public MyFilterAttribute(ILogger<MyFilterAttribute> logger){
this.logger = logger;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
var result = context.Result;
if (result is JsonResult json)
{
var x = json.Value;
var status = json.StatusCode;
this.logger.LogInformation(JsonConvert.SerializeObject(x));
}
if(result is ViewResult view){
// I think it's better to log ViewData instead of the finally rendered template string
var status = view.StatusCode;
var x = view.ViewData;
var name = view.ViewName;
this.logger.LogInformation(JsonConvert.SerializeObject(x));
}
else{
this.logger.LogInformation("...");
}
}
答案 1 :(得分:3)
我知道已经有一个答案,但是我还要补充一点,问题是MVC管道在运行Response.Body
时没有填充ActionFilter
,因此您无法访问它。 Response.Body
由MVC middleware
填充。
如果您想阅读Response.Body
,则需要创建自己的自定义中间件以在填充Response对象后拦截该呼叫。有许多网站可以向您展示如何执行此操作。一个示例是here。
如另一个答案中所述,如果您想在ActionFilter
中进行操作,则可以使用context.Result
来访问信息。
答案 2 :(得分:0)
您也可以...
string response = "Hello";
if (result is ObjectResult objectResult)
{
var status = objectResult.StatusCode;
var value = objectResult.Value;
var stringResult = objectResult.ToString();
responce = (JsonConvert.SerializeObject(value));
}
我在.net核心应用中使用了它。
希望有帮助。
答案 3 :(得分:0)
要在 ASP.NET Core筛选器管道中记录整个请求和响应,可以使用Result filter attribute
public class LogRequestResponseAttribute : TypeFilterAttribute
{
public LogRequestResponseAttribute() : base(typeof(LogRequestResponseImplementation)) { }
private class LogRequestResponseImplementation : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var requestHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Request.Headers);
Log.Information("requestHeaders: " + requestHeadersText);
var requestBodyText = await CommonLoggingTools.FormatRequestBody(context.HttpContext.Request);
Log.Information("requestBody: " + requestBodyText);
await next();
var responseHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Response.Headers);
Log.Information("responseHeaders: " + responseHeadersText);
var responseBodyText = await CommonLoggingTools.FormatResponseBody(context.HttpContext.Response);
Log.Information("responseBody: " + responseBodyText);
}
}
}
在Startup.cs中添加
app.UseMiddleware<ResponseRewindMiddleware>();
services.AddScoped<LogRequestResponseAttribute>();
在某处添加静态类
public static class CommonLoggingTools
{
public static async Task<string> FormatRequestBody(HttpRequest request)
{
//This line allows us to set the reader for the request back at the beginning of its stream.
request.EnableRewind();
//We now need to read the request stream. First, we create a new byte[] with the same length as the request stream...
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
//...Then we copy the entire request stream into the new buffer.
await request.Body.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
//We convert the byte[] into a string using UTF8 encoding...
var bodyAsText = Encoding.UTF8.GetString(buffer);
//..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
request.Body.Position = 0;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
public static async Task<string> FormatResponseBody(HttpResponse response)
{
//We need to read the response stream from the beginning...
response.Body.Seek(0, SeekOrigin.Begin);
//...and copy it into a string
string text = await new StreamReader(response.Body).ReadToEndAsync();
//We need to reset the reader for the response so that the client can read it.
response.Body.Seek(0, SeekOrigin.Begin);
response.Body.Position = 0;
//Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
return $"{response.StatusCode}: {text}";
}
public static string SerializeHeaders(IHeaderDictionary headers)
{
var dict = new Dictionary<string, string>();
foreach (var item in headers.ToList())
{
//if (item.Value != null)
//{
var header = string.Empty;
foreach (var value in item.Value)
{
header += value + " ";
}
// Trim the trailing space and add item to the dictionary
header = header.TrimEnd(" ".ToCharArray());
dict.Add(item.Key, header);
//}
}
return JsonConvert.SerializeObject(dict, Formatting.Indented);
}
}
public class ResponseRewindMiddleware {
private readonly RequestDelegate next;
public ResponseRewindMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
Stream originalBody = context.Response.Body;
try {
using (var memStream = new MemoryStream()) {
context.Response.Body = memStream;
await next(context);
//memStream.Position = 0;
//string responseBody = new StreamReader(memStream).ReadToEnd();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
} finally {
context.Response.Body = originalBody;
}
}