[英]WebAPI HttpContent convert to typed object
I am working on custom filter which should accomplish simple thing. 我正在研究自定义过滤器,它应该完成简单的事情。 All my APIs wrapped into 'Response' object. 我的所有API都包含在“Response”对象中。 I want to fill in all properties using filter. 我想使用过滤器填写所有属性。 This is code I have for the filter: 这是我对过滤器的代码:
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;
}
I found similar question where it was suggested to do var c = t.Result.Content.ReadAsAsync(typeof(Response<>));
我发现类似的问题,建议做var c = t.Result.Content.ReadAsAsync(typeof(Response<>));
but I can't do this because I can't make lambda function async
in this case. 但是我不能这样做因为在这种情况下我不能使lambda函数async
。
Any suggestion on how to get typed object out of HttpContent so I can assign properties before it returns to caller? 关于如何从HttpContent中获取类型对象的任何建议,以便我可以在返回调用者之前分配属性?
Here is Response<T>
这是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; }
}
EDIT 编辑
Here is how code looks now. 这是代码现在的样子。 I do get access to object, but webservice does not respond with data I fill to client. 我确实可以访问对象,但是webservice不响应我填写给客户端的数据。 It seems that code executed later after serialization/media formatting takes place. 似乎在序列化/媒体格式化之后执行代码。
I guess question becomes how do I add generic "handler" before web service returns but with access to beginning of call (so I can measure times, see request params, etc) 我想问题是如何在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);
EDIT 2: 编辑2:
Here is sample web api method which I want to intercept: 这是我要拦截的示例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;
}
All my APIs wrapped into 'Response' object. 我的所有API都包含在“Response”对象中。
First you can simplify your results by creating a implicit operators: 首先,您可以通过创建隐式运算符来简化结果:
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;
}
}
Now it should be easier to really ignore the repeated code of creating the response: 现在应该更容易真正忽略创建响应的重复代码:
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." }
};
}
}
Then you can create an 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;
}
}
}
Or Non-Core (MVC): 或非核心(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;
}
}
Or Non-Core (WebApi): 或非核心(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;
}
}
I tweaked your code. 我调整了你的代码。 I hope it helps. 我希望它有所帮助。 I couldn't check syntax errors though 我无法检查语法错误
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.