我正在研究应该完成简单事情的自定义过滤器。我所有的API都包装到“ Response”对象中。我想使用过滤器填写所有属性。这是我用于过滤器的代码:
public class MeteringFilter : IActionFilter
{
public Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
var attribute =
actionContext.ActionDescriptor.GetCustomAttributes<MeterAttribute>(true).SingleOrDefault() ??
actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<MeterAttribute>(true).SingleOrDefault();
if (attribute == null) return continuation();
var operation = actionContext.ActionDescriptor.ActionName;
var user = actionContext.RequestContext.Principal.Identity.Name;
var started = DateTimeOffset.Now;
return continuation().ContinueWith(t =>
{
var completed = DateTimeOffset.Now;
var duration = completed - started;
var c = t.Result.Content;
// This is code which does not work but I like to have:
// When debugger stops here I can see Content.Value and my object but I can't use this property like below
var cv = t.Result.Content.Value as Response<object>;
return t.Result;
});
}
public bool AllowMultiple => true;
}
我在建议执行var c = t.Result.Content.ReadAsAsync(typeof(Response<>));
的地方发现了类似的问题,但是我不能这样做,因为在这种情况下我无法使lambda函数async
。
关于如何从HttpContent中获取类型化对象的任何建议,这样我就可以在返回给调用者之前分配属性?
这里是Response<T>
public class Response<T>
{
public string Url { get; set; }
public DateTime ServerTime { get; set; }
public TimeSpan TimeTook { get; set; }
public T Data { get; set; }
public Error Error { get; set; }
}
编辑
这是现在的代码外观。我确实可以访问对象,但是Webservice不会响应我填充给客户端的数据。似乎是在序列化/媒体格式化之后执行的代码。
我想问题变成了如何在Web服务返回之前添加通用的“处理程序”,但可以访问调用的开始(因此我可以测量时间,查看请求参数等)
return continuation().ContinueWith(t =>
{
var c = t.Result.Content.ReadAsAsync(typeof(Response<object>), cancellationToken);
if (c.Result is Response<object> response)
{
Debug.WriteLine("Adding times");
response.ServerTime = startedOn;
response.TimeTook = DateTime.Now - startedOn;
}
return t.Result;
}, cancellationToken);
编辑2:
这是我想拦截的示例Web api方法:
[HttpGet]
public Response<LookupResponseData> Carrier(int? key = null, string id = "")
{
return this.GetKeyIdBundleForLookup("Carriers", key, id);
}
private Response<LookupResponseData> GetKeyIdBundleForLookup(string lookupId, int? key, string id)
{
if (!key.HasValue && string.IsNullOrEmpty(id))
return new Response<LookupResponseData>
{
Error = new Error { Code = ErrorCodes.InvalidQueryParameter, Message = "Either key or id must be specified" }
};
var r = new Response<LookupResponseData>();
try
{
this.LookupService.GetKeyIdDescription(this.AccountId, lookupId, key, id, out var keyResult, out var idResult, out var description);
if (!keyResult.HasValue)
return new Response<LookupResponseData>
{
Error = new Error { Code = ErrorCodes.InvalidOrMissingRecord, Message = "No record found for parameters specified" }
};
r.Data = new LookupResponseData { Key = keyResult.Value, Id = idResult, Description = description };
}
catch (Exception ex)
{
this.LoggerService.Log(this.AccountId, ex);
return new Response<LookupResponseData>
{
Error = new Error { Code = ErrorCodes.Unknown, Message = "API Call failed, please contact support. Details logged." }
};
}
return r;
}
答案 0 :(得分:3)
我所有的API都包裹在“ Response”对象中。
首先,您可以通过创建隐式运算符来简化结果:
public class Response
{
public string Url { get; set; }
public DateTime ServerTime { get; set; }
public TimeSpan TimeTook { get; set; }
}
public class Response<T> : Response
{
public T Data { get; set; }
public Error Error { get; set; }
public static implicit operator Response<TData>(TData data)
{
var result = new Response<TData>
{
Data = data,
};
return result;
}
public static implicit operator Response<TData>(Error error)
{
var result = new Response<TData>
{
Error = error,
};
return result;
}
}
现在,真正忽略创建响应的重复代码应该更容易:
private Response<LookupResponseData> GetKeyIdBundleForLookup(
string lookupId, int? key, string id)
{
if (!key.HasValue && string.IsNullOrEmpty(id))
return new Error
{
Code = ErrorCodes.InvalidQueryParameter,
Message = "Either key or id must be specified"
};
try
{
this.LookupService.GetKeyIdDescription(this.AccountId,
lookupId,
key,
id,
out var keyResult,
out var idResult,
out var description);
if (!keyResult.HasValue)
return new Error
{
Code = ErrorCodes.InvalidOrMissingRecord,
Message = "No record found for parameters specified"
};
return new LookupResponseData
{
Key = keyResult.Value,
Id = idResult, Description = description
};
catch (Exception ex)
{
this.LoggerService.Log(this.AccountId, ex);
return new Error
{
Code = ErrorCodes.Unknown,
Message = "API Call failed, please contact support. Details logged." }
};
}
}
然后您可以创建一个Core Async Action Filter:
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
// do something before the action executes
var started = DateTimeOffset.Now;
// Action Executes
var resultContext = await next();
// do something after the action executes; resultContext.Result will be set
if (result.Context.Result is Response response)
{
response.ServerTime = started;
response.TimeTook = DateTimeOffset.Now - started;
}
}
}
或非核心(MVC):
public class SampleActionFilter : ActionFilterAttribute
{
private const string TimerKey = nameof(SampleActionFilter ) + "_TimerKey";
public override void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Items[TimerKey] = DateTimeOffset.Now;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is Response response)
&& context.HttpContext.Items[TimerKey] is DateTimeOffset started)
{
response.ServerTime = started;
response.TimeTook = DateTimeOffset.Now - started;
}
}
或非核心(WebApi):
public class SampleActionFilter : ActionFilterAttribute
{
private const string TimerKey = nameof(SampleActionFilter ) + "_TimerKey";
public override void OnActionExecuting(HttpActionContext context)
{
context.Request.Properties[TimerKey] = DateTimeOffset.Now;
}
public override void OnActionExecuted(HttpActionExecutedContext context)
{
if (context.Result is Response response)
&& context.Request.Properties[TimerKey] is DateTimeOffset started)
{
response.ServerTime = started;
response.TimeTook = DateTimeOffset.Now - started;
}
}
答案 1 :(得分:0)
我调整了您的代码。希望对您有所帮助。 我虽然无法检查语法错误
return await continuation().ContinueWith(async t =>
{
var result = await t;
var c = await result.Content.ReadAsAsync(typeof(Response<object>), cancellationToken);
if (c is Response<object> response)
{
Debug.WriteLine("Adding times");
response.ServerTime = startedOn;
response.TimeTook = DateTime.Now - startedOn;
}
return result;
}, cancellationToken);