How Get Length of response in ASP.net Web API controller? - asp.net-web-api

In a WEBAPI filter, im trying to calculate response size.
A similar process works for MVC controllers.
Inside actionExecutedContext.Response. i cant see a filter?
So I tried this filter below but this doesnt work.
How can i get the length of a WEBApi response ?
I could stick this in Global.ASAX and it works, but then every http call is logged...
So an API filter would be ideal. Is there something obviously wrong here ?
public class BosAPIFilter : System.Web.Http.Filters.ActionFilterAttribute{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
base.OnActionExecuted(actionExecutedContext);
var httpContext = actionExecutedContext.Request.Properties["MS_HttpContext"] as HttpContextWrapper;
if (httpContext != null) {
actionExecutedContext.Response.
httpContext.Response.Filter = new ResponseStreamHandler(httpContext.Response.Filter);
var handler = httpContext.Response.Filter as ResponseStreamHandler;
var adminService = new AdminServices();
adminService.HttpTrace(httpContext, handler);
}
}
public class ResponseStreamHandler : MemoryStream {
private readonly Stream _responseStream;
public long ResponseSize { get; private set; }
public ResponseStreamHandler(Stream responseStream) {
this._responseStream = responseStream;
ResponseSize = 0;
}
public override void Write(byte[] buffer, int offset, int count) {
this.ResponseSize += count;
this._responseStream.Write(buffer, offset, count);
}
// ReSharper disable once RedundantOverridenMember
public override void Flush() { base.Flush(); }
}

In ASP.NET Web API pipeline, action filters run before the result you return from the action method gets serialized. If you look at actionExecutedContext.Response.Content inside the filter, it will be System.Net.Http.ObjectContent (depending on your action method). So, you can calculate the response size only later in the pipeline. You can use a message handler to do this but then the granularity is not at the action method level. The lowest granularity you can get is at a route level. One way you get around this is to set a flag in the request dictionary from the filter and log from the handler only when the flag is set.

Related

Android Retrofit2 calls to aspnet webapi are not triggering post when #SerializedName is added, but not passing object w/o it

I am trying to POST to a dotnet webapi from android retrofit. When I POST an object I am getting to the controller (I see the log entries in the console), but no values are getting passed. When I add #SerializedName, as I have had to do in other calls, the call is not even triggering the webapi controller post method.
My webapi controller looks like this:
[HttpPost]
public async Task<ActionResult<PageResults<TokenExchangeDTO>>> Post([FromBody] SearchFilter searchFilter)
{
_logger.LogDebug("Debug Post");
_logger.LogDebug("Post called: SearchPhrase=" + searchFilter.SearchPhrase +
";PageNumber=" + searchFilter.PageNumber + ";PageSize=" + searchFilter.PageSize);
return Ok(await Search(searchFilter));
}
I am trying to use this object as the input for the call:
public class PagingSearchDto {
#SerializedName("searchPhrase")
private String searchPhrase;
#SerializedName("pageNumber")
private int pageNumber;
#SerializedName("pageSize")
private int pageSize;
public void setSearchPhrase (String searchPhrase) { this.searchPhrase=searchPhrase; }
public String getSearchPhrase() { return this.searchPhrase; }
public void setPageNumber (int pageNumber) { this.pageNumber=pageNumber; }
public int getPageNumber() { return this.pageNumber; }
public void setPageSize(int pageSize) { this.pageSize=pageSize; }
public int getPageSize() { return this.pageSize; }
}
When I comment out the #SerializedName statements I can see the aspnet logging statements, but the object values are empty. When I add #SerializedName it looks like they don't get triggered at all.
I can see there is a request of some sort as I have ApplicationInsights on the dotnet webapi, so the request is seemingly made.
I have another POST working and set this one up in the exact same manner and this seems like strange behavior and hard to diagnose. I am following all the documentation and even have a working example in another POST.
I have an interceptor and see the post looks like this:
[PagingSearchDto{searchPhrase='', pageNumber=1, pageSize=10}]
I wish I could make it this to test:
{searchPhrase='', pageNumber=1, pageSize=10}
Is there any way to control how the json at the class level?

How to specify response type in ASP.NET Core middleware

My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}

Entity Framework 6 "DbContext has been disposed" exception

Something very strange is happening in production, and it only happens in production. I have a Web API running and in one of the APIs, there is a repository created in the constructor and used in the functions. This is how the flow of a request works:
HTTP request comes in
MVC API controller decides which "worker" class to instantiate and creates it using Activator.CreateInstance
API controller calls worker.OnExecute inside of a Task.Run() and returns the http response
Worker calls _engine.Execute
Each worker instantiates another "engine" class that has all of the logic.
The engine in case constructs 3 repositories created using a UnitOfWork that is created per engine instance, like so:
public class MyWorker : Worker
{
private readonly MyEngine _engine;
public MyWorker()
{
_engine = new MyEngine();
}
protected override WorkerResult OnExecute(JObject data, CancellationToken cta)
{
return new WorkerResult(HttpStatusCode.OK, _engine.Execute(data));
}
}
public class MyEngine : EngineBase
{
private BaseRepository<Order> OrderRepo { get; set; }
private BaseRepository<OrderItem> OrderItemRepo { get; set; }
public MyEngine()
{
OrderRepo = new BaseRepository<Order>(MyUnitOfWork);
OrderItemRepo = new BaseRepository<OrderItem>(MyUnitOfWork);
}
public string Execute(JObject data)
{
return IsOrderValid(data).ToString();
}
public bool IsOrderValid(JObject data)
{
var orderId = data.Value<int>("OrderId");
// Without this line it crashes. With this line it crashes
//OrderRepo = new BaseRepository<Order>(InternationalWork);
// This is where it crashes
Order order = OrderRepo.First(x => x.OrderID == orderId);
// more code
}
}
public class EngineBase : UnitOfWorker, IDisposable
{
private UnitOfWork _myUnitOfWork;
public EngineBase() { }
public UnitOfWork MyUnitOfWork
{
get
{
return _myUnitOfWork ?? (_myUnitOfWork = new UnitOfWork(new DbContextAdapter(new MyDbContext())));
}
}
}
This is the actual stack trace:
The operation cannot be completed because the DbContext has been disposed.
StackTrace1
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ObjectContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.CreateObjectQuery(Boolean asNoTracking, Nullable`1 streaming, IDbExecutionStrategy executionStrategy)
at System.Data.Entity.Internal.Linq.InternalSet`1.InitializeUnderlyingTypes(EntitySetTypePair pair)
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
The stack trace shows "FirstOrDefault" because OrderRepo.First internally calls DbSet.FirstOrDefault, like so:
public virtual T First(Expression<Func<T, bool>> query)
{
return _dbSet.FirstOrDefault(query);
}
I'm stumped because each worker is created per http request. Each DBContext is created per engine instance so I don't know how it could be disposed when it was just created in the constructor. And this only happens on the production web server where I presume it's being called more. Any tips would be greatly appreciated.

Adding custom data for an operation to Application Insights telemetry

I'm trying to add a bunch of custom data fields to every piece of telemetry I can, and this data is consistent across a single operation, but varies from operation to operation.
I have a custom ITelemetryInitializer, and within that I can do something like:
public class MyInitializer : ITelemetryInitializer
{
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = "PC";
}
}
But I don't understand how I'm suppose to push this data into this initializer.
I've added something like this:
public class MyInitializer : ITelemetryInitializer
{
private string mPlatform = "unknown";
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = mPlatform;
}
public void SetPlatform(string platform)
{
mPlatform = platform
}
}
And then at the controller level I do something like this:
foreach (var init in TelemetryConfiguration.Active.TelemetryInitializers)
{
var customInit = init as MyInitializer;
if (customInit != null)
{
customInit.SetPlatform(requestPlatform);
}
}
But this is horribly clunky, and prone to error (e.g. if a piece of telemetry gets sent before this function is called), and I'm not really sure if this is thread-safe.
What's the intended way of passing around this kind of data?
I think I've solved this now, the solution is to write to the properties of the TelemetryClient within the controller like this:
[Route( "[controller]" )]
public class MyController : Controller
{
private readonly TelemetryClient mTelemetryClient;
public MyController(
TelemetryClient TelemetryClientArg )
{
mTelemetryClient = TelemetryClientArg;
mTelemetryClient.Context.Properties.Remove("platform");
}
[HttpPost]
[Produces( "application/json" )]
public IActionResult Post( [FromBody] RequestClass RequestData )
{
mTelemetryClient.TrackTrace("Test trace 1"); // doesn't have platform set
mTelemetryClient.Context.Properties["platform"] = RequestData.platform;
mTelemetryClient.TrackTrace("Test trace 2"); // has platform set correctly
}
}
This seems to be safe as the controller constructor appears to be called before each http request is processed and the context within the TelemetryClient is unique per thread. I would like to get confirmation from the team that this is reasonable.

How can I log method expressions of JSF ajax requests

I have figured out how to log when a request is an ajax request and which page it is from, in a filter.
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="#form" listener="#{aboutYouController.findAddress}" .... )
How can I do this? My app has many ajax requests and I want to log which are being triggered.
public class TrackingFilter implements Filter {
private static Logger LOG = Logger.getLogger(TrackingFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
String pageHit = req.getRequestURI().substring(req.getContextPath().length()+1).replace(".xhtml", "");
if(!pageHit.contains("javax.faces.resource")){ // if is a url we want to log
if ("partial/ajax".equals(req.getHeader("Faces-Request"))) {
LOG.trace("ajax on URI: " + req.getRequestURI());
}
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="#form" listener="#{aboutYouController.findAddress}" ....)
This information is only available in the JSF component tree. The JSF component tree is only available after view build time. A view is only built when the request has been served by FacesServlet. Thus, a servlet filter is way too early as it runs before any servlet.
You'd better run the code after the restore view phase of a postback. The JSF component tree is guaranteed to be available during that moment. You can use FacesContext#isPostback() to check if the current request is a postback. You can use PartialViewContext#isAjaxRequest() to check if the current request is an ajax request. You can use the predefined javax.faces.source request parameter to obtain the client ID of the source component of the ajax request. You can use the predefined javax.faces.behavior.event request parameter to obtain the ajax event name (e.g. change, click, action, etc).
Obtaining the associated behavior listeners is in turn a story apart. This is easy on ActionSource2 components (e.g. <h|p:commandButton action="#{...}">) as the MethodExpression is just available by ActionSource2#getActionExpression(). However, this isn't easy on BehaviorBase taghandlers (e.g. <f|p:ajax listener="#{...}">) as this API doesn't have any method like getBehaviorListeners(). There are only methods to add and remove them, but not to obtain a list of them. So some nasty reflection trickery is necessary to access the private field with those listeners whose name is JSF implementation specific. In Mojarra it's listeners and in MyFaces it's _behaviorListeners. Both are fortunately assignable from List and it's the only field of that type, so we could just check for that. Once having hand of the BehaviorListener instance, then you still need to do another reflection trickery to obtain the MethodExpression field of that instance. Yuck.
All in all, here's how the trickery look like in flavor of a PhaseListener listening on afterPhase of RESTORE_VIEW:
public class AjaxActionLoggerPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (!(context.isPostback() && context.getPartialViewContext().isAjaxRequest())) {
return; // Not an ajax postback.
}
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String sourceClientId = params.get("javax.faces.source");
String behaviorEvent = params.get("javax.faces.behavior.event");
UIComponent source = context.getViewRoot().findComponent(sourceClientId);
List<String> methodExpressions = new ArrayList<>();
if (source instanceof ClientBehaviorHolder && behaviorEvent != null) {
for (ClientBehavior behavior : ((ClientBehaviorHolder) source).getClientBehaviors().get(behaviorEvent)) {
List<BehaviorListener> listeners = getField(BehaviorBase.class, List.class, behavior);
if (listeners != null) {
for (BehaviorListener listener : listeners) {
MethodExpression methodExpression = getField(listener.getClass(), MethodExpression.class, listener);
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
}
}
}
if (source instanceof ActionSource2) {
MethodExpression methodExpression = ((ActionSource2) source).getActionExpression();
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
System.out.println(methodExpressions); // Do your thing with it.
}
private static <C, F> F getField(Class<? extends C> classType, Class<F> fieldType, C instance) {
try {
for (Field field : classType.getDeclaredFields()) {
if (field.getType().isAssignableFrom(fieldType)) {
field.setAccessible(true);
return (F) field.get(instance);
}
}
} catch (Exception e) {
// Handle?
}
return null;
}
}
In order to get it to run, register as below in faces-config.xml:
<lifecycle>
<phase-listener>com.example.AjaxActionLoggerPhaseListener</phase-listener>
</lifecycle>
Above is tested and compatible with Mojarra and PrimeFaces and theoretically also compatible with MyFaces.
Update: in case you're using JSF utility library OmniFaces, or are open to, since version 2.4 you can use the new Components#getCurrentActionSource() utility method to find out the current action source component and Components#getActionExpressionsAndListeners() to get a list of all action methods and listeners registered on a given component. This is also useable on regular (non-ajax) requests. With that, the above PhaseListener example can be reduced as below:
public class FacesActionLoggerPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.PROCESS_VALIDATIONS;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
if (!event.getFacesContext().isPostback())) {
return;
}
UIComponent source = Components.getCurrentActionSource();
List<String> methodExpressions = Components.getActionExpressionsAndListeners(source);
System.out.println(methodExpressions); // Do your thing with it.
}
}

Resources