Mini profiler not displaying ajax request information? - asp.net-mvc

I'm using the mini-profiler on my asp.net MVC 3 application. I've implemented the profiler using the mvc nuget package. Everything works ok for standard page requests i get profile information sql everything.
However ajax requests don't seem to display initially. Just to let me confirm the request are completed without error. I've debugged this and they complete they also return http 200 responses in fiddler.
There is no request by the mini profiler that accompanies the ajax request. When i then navigate to another page i.e. a standard page request all the ajax request that were made on the last page are now displayed.
This is my mini profiler configuration page in App_Start
public static class MiniProfilerPackage
{
public static void PreStart()
{
//Setup sql formatter
MiniProfiler.Settings.SqlFormatter = new OracleFormatter();
//Make sure the MiniProfiler handles BeginRequest and EndRequest
DynamicModuleUtility.RegisterModule(typeof(MiniProfilerStartupModule));
//Setup profiler for Controllers via a Global ActionFilter
GlobalFilters.Filters.Add(new ProfilingActionFilter());
//Settings
MiniProfiler.Settings.PopupShowTimeWithChildren = true;
MiniProfiler.Settings.PopupShowTrivial = false;
//Ignore glimpse details in miniprofiler
var ignored = MiniProfiler.Settings.IgnoredPaths.ToList();
ignored.Add("Glimpse.axd");
MiniProfiler.Settings.IgnoredPaths = ignored.ToArray();
}
public static void PostStart()
{
// Intercept ViewEngines to profile all partial views and regular views.
// If you prefer to insert your profiling blocks manually you can comment this out
var copy = ViewEngines.Engines.ToList();
ViewEngines.Engines.Clear();
foreach (var item in copy)
{
ViewEngines.Engines.Add(new ProfilingViewEngine(item));
}
}
}
public class MiniProfilerStartupModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += (sender, e) =>
{
var request = ((HttpApplication)sender).Request;
MiniProfiler.Start();
};
//Profiling abadened if user is not in the admin role
context.PostAuthorizeRequest += (sender, e) =>
{
if (!context.User.IsInRole("Admin"))
MiniProfiler.Stop(discardResults: true);
};
context.EndRequest += (sender, e) =>
{
MiniProfiler.Stop();
};
}
public void Dispose() { }
}
Is there some configuration that is required or potential issues I should be aware of?

In the end i figured out that the Miniprofiler JavaScript block that is added to the page must be below the pages jquery reference.
My JavaScript reference are all added at the end of the page for best practice performance reasons.
But I'd left the #MvcMiniProfiler.MiniProfiler.RenderIncludes() in the page header. Moving it below the jquery script ref at the bottom of the page fixed my problem.

Just found out that the same situation repeat if
$.ajaxSetup({
global: false
});

Related

Using events to trigger ActionResult in ASP.NET MVC

I am working on an ASP.NET MVC web service. In a web page, when a user clicks on a button, this triggers a complex method that takes a bit of time to finish. I want to redirect the user to a waiting page and then, when the process is finished, to redirect the user to a new page.
When the process is done it raises an event, which I can listen to from the controller. But I cannot make the last step to work (the controller redirecting to the new page upon receiving the event).
Here is my very naïve attempt at doing it (with simpler names):
public MyController()
{
EventsControllerClass.ProcessComplete += new EventHandler<MyArgsClass>(OnEventReceived);
}
private void OnEventReceived(object sender, MyArgsClass eventArguments)
{
RedirectToPage();
}
private ActionResult RedirectToPage()
{
return RedirectToAction("PageName");
}
After many days working on this, I have a viable solution. It may not be pretty, but it works, and maybe some ideas can be useful for other people, so here it goes:
I will explain the solution to my particular problem: I need a button to redirect to a "waiting" page while a longer process runs in the background and raises an event when it is finished. When this event is received, we want to redirect the user (automatically) to a final page.
First, I created a class to listen to the event. I tried doing this directly in the controller, but you need to be careful about signing and unsigning, because apparently controllers get created and destroyed at each request. In this "listener class" I have a bool property that is set to "true" when the event is received.
When the first action is triggered, the controller normally redirects to the "wait" page, where I have this simple java script redirecting to the new action:
<script type="text/javascript">
window.location = "#Url.Action("WaitThenRedirect", "AuxiliaryControllerName")";
</script>
This sets in motion the long process (through another event). The key is that I do this with an asynchronous action (this controller inherits from AsyncController). (Note I used an auxiliary controller. This is to keep all asynchronous stuff apart.) This is how this looks (more info here):
public static event EventHandler<AuxiliaryEventsArgs> ProcessReady;
public void WaitThenRedirectAsync()
{
AsyncManager.OutstandingOperations.Increment();
ProcessReady += (sender, e) =>
{
AsyncManager.Parameters["success"] = e.success;
AsyncManager.OutstandingOperations.Decrement();
};
WaitForEvent();
}
public ActionResult WaitThenRedirectCompleted(bool success)
{
if (success)
{
return RedirectToAction("RedirectToView", "ControllerName");
}
else
{
return RedirectToAction("UnexpectedError", "ControllerName");
}
}
private void WaitForEvent()
{
bool isWaitSuccessful = true;
int waitingLoops = 0;
int waitingThreshold = 200;
int sleepPeriod = 100; // (milliseconds)
while (!EventsListener.IsTheThingReady())
{
System.Threading.Thread.Sleep(sleepPeriod);
++waitingLoops;
if (waitingLoops > waitingThreshold)
{
System.Diagnostics.Debug.WriteLine("Waiting timed out!");
isWaitSuccessful = false;
break;
}
}
isWaitSuccessful = true;
if (null != ProcessReady)
{
AuxiliaryEventsArgs arguments = new AuxiliaryEventsArgs();
arguments.success = isWaitSuccessful;
try
{
ProcessReady(null, arguments);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Error in event ProcessReady" + ex);
}
}
}
I believe it is possible to use ajax syntax for alternative solutions, but this is what I have and it works nicely. I believe this is not a very common need, but hopefully someone will benefit!

Dynamic internal redirect in ASP.NET MVC

I've searched a lot but didn't find any solution. So here is my case:
I have database model UrlRedirect
public class UrlRedirect : AuditInfo
{
[Key]
public int Id { get; set; }
public string OldUrl { get; set; }
public string NewUrl { get; set; }
}
As you may assume I am trying to save URL mapping between OldUrl and NewUrl.
I want to internally change the OldUrl path of the request to the NewUrl and then run all defined routes as they will run if the user is opening NewUrl directly.
The redirect should be server side URL rewrite and user should see the old URL in their browser
In Global.asax you have some events that are executed in the web application context. (for sample: Start_Application, End_Application, etc).
In your case, you could use the BeginRequest event, where every request made to your web application is executed. In this event you could check the URL and try to redirect it using the default of htpp protocols, such as 301 Moved Permanently status code. For sample, in the Global.asax file, add the code bellow:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
// get the current url
string currentUrl = HttpContext.Current.Request.Url.ToString().ToLower();
// here, you could create a method to get the UrlRedirect object by the OldUrl.
var urlRedirect = GetUrlRedirect(currentUrl);
// check if the urlRedirect object was found in dataBase or any where you save it
if (urlRedirect != null)
{
// redirect to new URL.
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", urlRedirect.NewUrl);
Response.End();
}
}
In the sample, GetUrlRedirect(string) method should check it in a database, xml file, cache, or anywhere you save the UrlRedirect objects.
To understand more about how asp.net core applications life cycles works, read this article.
If you really want to redirect within the server you may use one of the following:
HttpServerUtility.TransferRequest
HttpServerUtility.Transfer
HttpServerUtility.Execute
HttpContext.RewritePath
You can read more about these options, where a similar problem is posed here.
Consider the impact of your choice on the request serving performance. IMHO you should try your best to utilize MVC infrastructure of Routing, or just fall back to simple redirections (even permanent for speed) as [user:316799] wrote when you compute new urls in business layer or map from db.
This is my final solution that works like a charm. Thanks to #WeTTTT for the idea.
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
// ...
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
this.ApplyCustomUrlRedirects(new UowData(), HttpContext.Current);
}
private void ApplyCustomUrlRedirects(IUowData data, HttpContext context)
{
var currentUrl = context.Request.Path;
var url = data.UrlRedirects.All().FirstOrDefault(x => x.OldUrl == currentUrl);
if (url != null)
{
context.RewritePath(url.NewUrl);
}
}
}

Writing to a HttpResponse with BufferOutput=false blocks when an ActionResult comes from a ASP.NET MVC 4 asynchronous action and Glimpse is enabled

I have a custom ActionResult that sets HttpResponse.BufferOutput = false and then writes data to the response stream. I noticed that when the action result comes from a task-based asynchronous action method in ASP.NET MVC, writing to the response stream blocks. This happens only when Glimpse plugin is enabled in web.config. Glimpse is very useful to me, I really want to have it enabled, at least during development and testing. BufferOutput property should remain false, because the content length can be quite large and I don't want to buffer it in memory.
This is the shortest code that could reproduce this exact behaviour:
public sealed class CustomResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
var resp = context.HttpContext.Response;
resp.BufferOutput = false;
resp.ContentType = "text/plain";
resp.Output.Write(DateTime.UtcNow.ToString());
resp.Flush();
}
}
public sealed class DownloadController : Controller
{
// the client nevers gets the response from this action
public async Task<ActionResult> Async()
{
await Task.Yield();
return new CustomResult();
}
// this works
public ActionResult Sync()
{
return new CustomResult();
}
}
I tested this with the latest Glimpse.Mvc4 package (version 1.3.2).
Am I doing something wrong, is there a workaround to this issue or is this a Glimpse bug and I should report it?

SharePoint Publishing site HTTPModule

I have written an HTTPModule for the redirection purpose and installed in GAC and referenced in root web.config file. It is working for Team sites very well.
I am using PreRequestHandlerExecute to see the request is page or not and calling
public void Init(HttpApplication context)
{
this.app = context;
this.app.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute);
}
void Application_PreRequestHandlerExecute(object source, EventArgs e)
{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null)
{
page.PreInit += new EventHandler(Perform_Redirection);
}
}
and in the Perform_Redirection method I am doing the redirection stuff.
void Perform_Redirection(object source, EventArgs e)
{
//logic goes here for redirection
}
The above code working fine for Teamsites but not for Publishing sites. The Page.PreInit is not firing for publishing sites.
Please help me to solve this problem!
I am using PreRequestHandlerExecute, because I need session object and other details otherwise I would have used BeginRequest.
I solved it by moving the redirection code into the PreRequestHandlerExecute event handler

ASP.NET MVC Session Expiration

We have an internal ASP.NET MVC application that requires a logon. Log on works great and does what's expected. We have a session expiration of 15 minutes. After sitting on a single page for that period of time, the user has lost the session. If they attempt to refresh the current page or browse to another, they will get a log on page. We keep their request stored so once they've logged in they can continue on to the page that they've requested. This works great.
However, my issue is that on some pages there are AJAX calls. For example, they may fill out part of a form, wander off and let their session expire. When they come back, the screen is still displayed. If they simply fill in a box (which will make an AJAX call) the AJAX call will return the Logon page (inside of whatever div the AJAX should have simply returned the actual results). This looks horrible.
I think that the solution is to make the page itself expire (so that when a session is terminated, they automatically are returned to the logon screen without any action by them). However, I'm wondering if there are opinions/ideas on how best to implement this specifically in regards to best practices in ASP.NET MVC.
Update:
So I went ahead and implemented this in my OnActionExecuting (per Keltex's suggestion)
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.Write("Invalid session -- please login!");
filterContext.HttpContext.Response.End();
}
else
{
...
}
}
This definitely makes things better -- now even if they have two tabs (one with some AJAX calls that they can trigger) and they log out explicitly in the second tab, they will immediately get something that makes more sense rather than a bunch of screwed up AJAX data.
I still think I will implement the Javascript countdown as well that womp suggested.
Specifically, I don't know that there are any best practices regarding it, but I'm doing this right now for our app. We've opted for a client-side solution where we output the Session timeout value into some javascript in the master page, and calculate when the session will expire.
5 minutes before-hand, we pop up a modal dialog box saying "Are you still there?" with a countdown timer. Once the timer hits 0:00, we redirect the browser to the login page.
It's implemented with a minimal amount of javascript to do the time and timer calculations, and a simple .ashx handler that will refresh the session if the user clicks "I'm back!" on the dialog box before the session expires. That way if they return in time, they can refresh the session without any navigation.
I asked similar question yesterday. Here is my solution:
Modified Authorize attribute:
public class OptionalAuthorizeAttribute : AuthorizeAttribute
{
private class Http403Result : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
// Set the response code to 403.
context.HttpContext.Response.StatusCode = 403;
context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue);
}
}
private readonly bool _authorize;
public OptionalAuthorizeAttribute()
{
_authorize = true;
}
//OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller.
//That is why parameter is introduced.
public OptionalAuthorizeAttribute(bool authorize)
{
_authorize = authorize;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//When authorize parameter is set to false, not authorization should be performed.
if (!_authorize)
return true;
var result = base.AuthorizeCore(httpContext);
return result;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
//Ajax request doesn't return to login page, it just returns 403 error.
filterContext.Result = new Http403Result();
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
HandleUnauthorizedRequest is overridden, so it returns Http403Result when using Ajax. Http403Result changes StatusCode to 403 and returns message to the user in response. There is some additional logic in attribute (authorize parameter), because I turn on [Authorize] in base controller and disable it in some pages.
Other important part is global handling of this response on client side. This is what I placed in Site.Master:
<script type="text/javascript">
$(document).ready(
function() {
$("body").ajaxError(
function(e,request) {
if (request.status == 403) {
alert(request.responseText);
window.location = '/Logout';
}
}
);
}
);
</script>
I place GLOBAL ajax error handler and when evert $.post fails with 403 error, response message is alerted and user is redirected to logout page. Now I don't have to handle error in every $.post request, because it is handled globally.
Why 403, not 401? 401 is handled internally by MVC framework (that is why redirection to login page is done after failed authorization).
What do you think about it?
EDIT:
About resigning from [Authorize] attribute: [Authorize] is not only about checking Identity.IsAuthenticated. It also handles page caching (so you don't cache material that requires authentication) and redirection. There is no need to copy this code.
You might look into the AjaxOptions that can be set in Ajax.BeginForm(). There is an OnBegin setting that you can associate with a javascript function, which could call a Controller method to confirm that the session is still valid, and if not, redirect to the login page using window.location.
Part of the problem appears to be that you're letting the framework do everything. I wouldn't decorate your AJAX method with the [Authorize] attribute. Instead check User.Identity.IsAuthenticated and if it returns false, create sensible error message.
My solution uses one meta-tag on login form and a bit of Javascript/jQuery.
LogOn.cshtml
<html>
<head>
<meta data-name="__loginform__" content="true" />
...
</head>
...
</html>
Common.js
var Common = {
IsLoginForm: function (data) {
var res = false;
if (data.indexOf("__loginform__") > 0) {
// Do a meta-test for login form
var temp =
$("<div>")
.html(data)
.find("meta[data-name='__loginform__']")
.attr("content");
res = !!temp;
}
return res;
}
};
AJAX code
$.get(myUrl, myData, function (serverData) {
if (Common.IsLoginForm(serverData)) {
location.reload();
return;
}
// Proceed with filling your placeholder or whatever you do with serverData response
// ...
});
Here's how I did it...
In my base controller
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.HttpContext.Response.Write(SessionTimeout);
filterContext.HttpContext.Response.End();
}
}
}
Then in my global .js file
$.ajaxSetup({
error: function (x, status, error) {
if (x.status == 403) {
alert("Sorry, your session has expired. Please login again to continue");
window.location.href = "/Account/Login";
}
else {
alert("An error occurred: " + status + "nError: " + error);
}
}
});
The SessionTimeout variable is a noty string. I omitted the implementation for brevity.

Resources