MiniProfiler: How do I profile an AngularJS + WebAPI app?
Asked Answered
S

1

10

Cross-posted on the MiniProfiler community.

I'm trying to throw MiniProfiler into my current stack. I think I'm mostly setup, but am missing the UI approach and would like recommendations on the best way to proceed.

Current Stack

  • SQL for DB (including MiniProfiler tables)
  • EF 6
  • WebAPI 2 API app
  • Angular 1.x. app for the UI (separate app, no MVC backing it) -- I think it's 1.5.x at this point.

So, the current method of RenderIncludes() isn't available to me.

What's the best method to include the JS files and set them up to retrieve the information from the SQL Server storage? I know that the files are included in the UI repo, but I didn't see docs for explicit configuration.

What I've Tried So Far -- Web API App

  • Installed the MiniProfiler and MiniProfiler.EF6 packages.

Web.Config -- Added Handler

(not sure if this is necessary):

<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />

Added a CORS filter to expose the MiniProfiler IDs to my client app:

public class AddMiniProfilerCORSHeaderFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        actionExecutedContext.Response.Headers.Add("Access-Control-Expose-Headers", "X-MiniProfiler-Ids");
    }
}

Add that filter as part of startup:

config.Filters.Add(new AddMiniProfilerCORSHeaderFilter());`

In Global.asax, added to Application_Start():

var connectionString = ConfigurationReader.GetConnectionString(Constants.ConfigSettings.CONNECTION_STRING_NAME);

MiniProfiler.Settings.Storage = new SqlServerStorage(connectionString);
MiniProfilerEF6.Initialize();

Update the begin/end requests:

   protected void Application_BeginRequest()
    {
        if (Request.IsLocal || ConfigurationReader.GetAppSetting(Constants.ConfigSettings.USE_PROFILER, false))
        {
            var sessionId = Guid.NewGuid().ToString();
            MiniProfiler.Start(sessionId);
        }
    }

    protected void Application_EndRequest()
    {
        MiniProfiler.Stop();
    }

What I've tried so far -- client (Angular) App

  • Snagged the UI files from the Github Repo
  • Copied the UI directory to my project's output

Reference the CSS:

<link rel="stylesheet" href="js/lib/miniprofiler/includes.css" />

Call the JavaScript

  <script async type="text/javascript" 
    id="mini-profiler" 
    src="js/lib/miniprofiler/includes.js?v=1.0.0.0" 
    data-current-id="" 
    data-path="https://localhost:44378/api/profiler/" 
    data-children="true" 
    data-ids="" 
    data-version="1.0.0.0" 
    data-controls="true" 
    data-start-hidden="false" 
    data-trivial-milliseconds="5">
  </script>

Current Status

When I do these things, it looks like it just can't find the appropriate WebAPI controller to render the result. If I can figure out where that controller is or replicate it (as I'm attempting to do currently) I think I'll be in business.

Superpose answered 7/7, 2016 at 15:26 Comment(0)
C
1

The RenderIncludes function results in a <script> tag being output to the page. It is defined in the UI Repo as include.partial.html and currently looks like this:

<script async type="text/javascript" id="mini-profiler" 
        src="{path}includes.js?v={version}" data-version="{version}" 
        data-path="{path}" data-current-id="{currentId}" 
        data-ids="{ids}" data-position="{position}" 
        data-trivial="{showTrivial}" data-children="{showChildren}" 
        data-max-traces="{maxTracesToShow}" data-controls="{showControls}"
        data-authorized="{authorized}" data-toggle-shortcut="{toggleShortcut}" 
        data-start-hidden="{startHidden}" data-trivial-milliseconds="{trivialMilliseconds}">
</script>

This is the piece of Javascript that runs the rendering.

The RenderIncludes function is defined here. It does the following:

  1. Makes sure that you have storage set up
  2. Checks that current request is authorized to view results
  3. Gets the Ids of the unviewed profiles for the current user
  4. Takes the Ids along with any other params that you passed into the function and inserts them into the placeholders in the script defined in include.partial.html
  5. Outputs this <script>

So if you cannot call RenderIncludes, there should be no reason why you cannot just put the script file in place, retrieve the unviewed Ids, but them along with any other setup values you want into the <script> tag, and output the tag.

The key lines of code for retrieving the Id values are:

var ids = authorized 
            ? MiniProfiler.Settings.Storage.GetUnviewedIds(profiler.User) 
            : new List<Guid>();
ids.Add(profiler.Id);

where profiler is the current instance of MiniProfiler (run on the current request.

You will also probably need to make sure that you can handle the call that the script will make to /mini-profiler-resources/results (passing in the id of the profiler as a param). The guts of this is located here in the GetSingleProfilerResult(HttpContext context) function

Christan answered 7/7, 2016 at 20:50 Comment(7)
Thanks for this! I just posted a large update documenting my current approach. Do you think the answer is still applicable in that context?Superpose
@Superpose your approach should be good, but it will do nothing if you don't include the id values in the script.Christan
yep, understood. I don't really have them by default as I don't know that I can put the headers into the data attribute on the element before the script loads. It does pick them up on the next call and attempts to hit an endpoint to retrieve the data for them, which is where it fails. Trying to figure out if I need to create that endpoint or just surface an existing/default one somehow.Superpose
retrieving the data goes to /mini-profiler-resources/results including the id value as a form element. So you need to simulate this as wellChristan
ahhh, that makes perfect sense, Thanks! I'll give it a shot ASAP when I'm back in front of the code.Superpose
I added info to the bottom of my answer for where in the code results are currently retrievedChristan
I got this working using the Minipoller libs in a servicestack project, so using it within WebAPI should actually be simpler. For anyone interested, I document the steps here inayearorso.io/2018/04/18/…Grunberg

© 2022 - 2024 — McMap. All rights reserved.