How to enable WebApi DelegatingHandler via web.config - c#

In order to log the JSON of each WebApi request/response I have created a custom DelegatingHandler and added this to the MessageHandlers collection in WebApiConfig.cs and it works great.
In future though, I'd like to be able to enable this handler on other WebApi applications via web.config without having to actually modify WebApiConfig.cs.
By way of clarification, what I'm trying to achieve is analogous to what was possible in WCF where you could create a completely separate dll, drop it into the bin folder of a WCF service and add it into the WCF pipeline solely by editing the web.config file without having to modify the source of the service at all.
Is this possible in WebApi or can a custom DelegatingHandler only be added via code at runtime?

Modify the handler to check the config and perform its function if enabled otherwise just let the request pass through. if being used for logging make sure it is added early in the pipeline. Look into using middle-ware if possible.
public class LoggingHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
var appSetting = ConfigurationManager.AppSettings["LoggingHandlerEnabled"];
var enabled = true;
bool.TryParse(appSetting, out enabled);
if(enabled) {
//...Extract and log request
LogRequest(request);
}
// Execute the request and get the response
var response = await base.SendAsync(request, cancellationToken);
if(enabled) {
//...Extract details from response for logging
LogResponse(response);
}
return response;
}
private void LogRequest(HttpRequestMessage request) {
//... code removed for brevity
}
private void LogResponse(HttpResponseMessage response) {
//... code removed for brevity
}
}
With that in place then there would be no further need to modify any more code to enable/disable the handler. Update the config file and the handler will respect the setting.

After a bit of research I am answering my own question. It seems like this is not possible without having to modify the source of the target application. It might be possible to dynamically load or inject such a handler at startup if some thought was given during the writing of the application and knew to look for it. Another possible solution would be to create the logging handler as a nuget package and when the nuget package is installed into the target application the installer would add the dll and also create a WebActivator hook that added the handler to the MessagingHandlers collection in PostApplicationStartMethod. This might be the approach that involves that least amount of manual code change, but would still require recompilation and re-deployment of the target app.

Tracing in ASP.NET Web API 2
From the Tools menu, select Library Package Manager, then Package Manage Console.
In the Package Manager Console window, type the following commands.
Install-Package Microsoft.AspNet.WebApi.Tracing
Update-Package Microsoft.AspNet.WebApi.WebHost
The first command installs the latest Web API tracing package. It also updates the core Web API packages. The second command updates the WebApi.WebHost package to the latest version.
Open the file WebApiConfig.cs in the App_Start folder. Add the following code to the Register method.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code
config.EnableSystemDiagnosticsTracing();
// Other configuration code not shown.
}
}
This code adds the SystemDiagnosticsTraceWriter class to the Web API pipeline. The SystemDiagnosticsTraceWriter class writes traces to System.Diagnostics.Trace.
To see the traces, run the application in the debugger. In the browser, navigate to /api/values.
The trace statements are written to the Output window in Visual Studio. (From the View menu, select Output).

Related

Is Azure storage supported in Azure Static Web App functions?

I'm working on an API for an Azure Static Web App. The web app is implemented in Angular (although that isn't important for this question), and the API is implemented in C# (NET 6). Deployment to Azure is via a GitHub action.
I can create an HTTP trigger API endpoint that works fine, like so:
public static class Tester
{
[FunctionName("Tester")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/tester")] HttpRequest req,
ILogger log)
{
return new OkObjectResult("Hello World");
}
}
I'm also able to access this directly via the SWA URL: https://<sitename>.azurestaticapps.net/api/v1/tester.
However, as soon as I add a reference to an Azure storage NuGet package to the project file (specifically Microsoft.Azure.WebJobs.Extensions.Storage.Blobs), making no other changes to the code, the API endpoint no longer works once deployed (although it will work locally).
On deploying the code with that package referenced in the .csproj, hitting the API endpoint gives a 503 status code with the response:
Function host is not running.
I enabled Application Insights for this static web app, and a CryptographicException is being thrown on startup:
An error occurred while trying to encrypt the provided data. Refer to the inner exception for more information. For more information go to http://aka.ms/dataprotectionwarning Could not find any recognizable digits.
(The link in the message doesn't go anywhere useful).
I'm presuming this has something to do with the AzureWebJobsStorage setting, which cannot be set in an Azure Static Web App (for whatever reason).
Based on all of the above, it would seem that using Azure storage from within a static web app C# function is verboten. However, I can't find that stated explicitly online anywhere. Has anybody got this kind of thing to work?
I removed the following nuget packages to make it working:
Microsoft.Azure.EventGrid
Microsoft.Azure.WebJobs.Extensions.EventGrid
I decomposed my http functions to a separate project because SWA does not support the EventTriggers right now.

How to track dependencies, exceptions and requests for Live Metrics in ASP.NET MVC app?

I've configured Live Metrics for my ASP.NET MVC app with target framework 4.7.2 using the tutorial given in Microsoft Docs:
https://learn.microsoft.com/en-us/azure/azure-monitor/app/live-stream#enable-livemetrics-using-code-for-any-net-application
In this tutorial, they've given a sample client.TrackDependency() and client.TrackRequest() call in the end. They've also mentioned in comments that those are samples and we must replace it with actual application logic to work. I'm new to all these and I don't know what to replace. Since my application is huge and has a lot of methods, it is impractical to call the tracking methods in each method or controller. Since it is not ASP.NET Core, there are no middlewares and I have to enable Live Metrics by code too. I've added the code in the Application_Start() of Global.asax.cs of my application, so that it runs during startup.
This is what I've done so far,
// Create a TelemetryConfiguration instance.
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration telemetryConfig = Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CreateDefault();
telemetryConfig.InstrumentationKey = System.Web.Configuration.WebConfigurationManager.AppSettings["AppInsightsInstrumentationKey"];
QuickPulseTelemetryProcessor quickPulseProcessor = null;
telemetryConfig.DefaultTelemetrySink.TelemetryProcessorChainBuilder
.Use((next) =>
{
quickPulseProcessor = new QuickPulseTelemetryProcessor(next);
return quickPulseProcessor;
})
.Build();
var quickPulseModule = new QuickPulseTelemetryModule();
// Secure the control channel.
// This is optional, but recommended.
//quickPulseModule.AuthenticationApiKey = "YOUR-API-KEY-HERE";
quickPulseModule.Initialize(telemetryConfig);
quickPulseModule.RegisterTelemetryProcessor(quickPulseProcessor);
// Create a TelemetryClient instance. It is important
// to use the same TelemetryConfiguration here as the one
// used to setup Live Metrics.
TelemetryClient client = new TelemetryClient(telemetryConfig);
// I need some method by which I can track all the requests, exceptions,
// dependencies etc. here.
I searched and searched a lot for a solution but couldn't get a concrete solution. As a last resort I'm requesting you guys to help me. What can I do to track all requests, dependencies, exceptions, etc. globally...?
If you're using the ASP .NET MVC with .Net Framework 4.7.2 Version, You need to configure the Application Insights code related to the .NET Specific SDK Type like Framework, Core, Console, etc.
From the given MS doc, you're following the console app related app insights code but as you're using the MVC Web App so you need to follow this code from this section of documentation.
Here is the workaround I tried to get the live metrics, logs in the Application Insights of Azure Portal.
In Visual Studio, Created the asp .net mvc web app (.NET Framework version 4.7.2)
Added the Application Insights Instrumentation Key in the ApplicationInsights.config
Follow the above documentation as it says to create a new folder in the root and add the ErrorHandler class and modify the FilterConfig class from the App_Start folder in order to match your ErrorHandler Class Functionality.
And then deploy the MVC Web App. While publishing configure the Application Insights in Visual Studio Publish Window like below:
After deploying the App, Open the Web App URL in the browser, then you can see the logs in overview tab and also in App Insights Resource Live Metrics Page as you can see the screenshots below:
I found a solution myself. I found out that I can use the Application_BeginRequest() event handler to catch all requests inside Global.asax itself. All I had to do is to store the TelemetryConfiguration into a global variable and access it from the Application_BeginRequest() handler. This is what I did:
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse;
protected void Application_Start()
{
RegisterLiveMetrics();
// Omitted the other code for brevity
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
var telemetryConfig = Application["TelemetryConfig"] as TelemetryConfiguration;
TelemetryClient client = new TelemetryClient(telemetryConfig);
var httpContextCurrent = HttpContext.Current;
client.TrackRequest(httpContextCurrent.Request.RawUrl, DateTimeOffset.Now,
TimeSpan.FromMilliseconds(230), httpContextCurrent.Response.StatusCode.ToString(),
true);
}
private void RegisterLiveMetrics()
{
// Create a TelemetryConfiguration instance.
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration telemetryConfig = Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CreateDefault();
telemetryConfig.InstrumentationKey = System.Web.Configuration.WebConfigurationManager.AppSettings["AppInsightsInstrumentationKey"];
QuickPulseTelemetryProcessor quickPulseProcessor = null;
telemetryConfig.DefaultTelemetrySink.TelemetryProcessorChainBuilder
.Use((next) =>
{
quickPulseProcessor = new QuickPulseTelemetryProcessor(next);
return quickPulseProcessor;
})
.Build();
var quickPulseModule = new QuickPulseTelemetryModule();
quickPulseModule.Initialize(telemetryConfig);
quickPulseModule.RegisterTelemetryProcessor(quickPulseProcessor);
Application["TelemetryConfig"] = telemetryConfig;
}
Luckily this seems to work fine. Currently I'm only tracking requests.
Note: I'm not sure about the namespaces mentioned above.

How to read event data information from VSTS web hooks

We are currently developing a service that integrates service now and VSTS. When an incident created in servicenow, based on the category selected the integration creates a work item in the VSTS. This part we were able to accomplish. Now we need to send updates from VSTS workitems, if any, to servicenow incident. We have created a service hook that captures the work item event details when an update happens to work item.
Now,the problem that I am having is, how can I create an event listener that listens to the web hook and reads that information. Is an example available on how to create the event listener from web hooks?
I implemented something similar with github webhooks. I´ve used an asp.net web api in azure because in .netcore it´s still prerelease. You can look for it githubwebhookrepo
There is also a nuget package for vsts. I think it should be pretty much the same.
nuget
You will get a context with a payload json in it and there should be your event type.
public class GitHubWebHookHandler : WebHookHandler
{
public GitHubWebHookHandler()
{
this.Receiver = GitHubWebHookReceiver.ReceiverName;
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
JObject entry = context.GetDataOrDefault<JObject>();
return Task.FromResult(true);
}
}
Payload info: https://learn.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=vsts

Azure Cloud Service - Monitoring a deployment

Is there an event raised the moment a website is published and updated?
I've tried Application_End in global.asax but that event does not seem to be raised.
I suggest to use both Kudu and Microsoft ASP.NET WebHooks preview.
Kudu is the engine behind git deployments, WebJobs, and various other features in Azure Web Sites (Kudu source is on GitHub)
With Kudu, Azure Web Sites have a support for web hooks. There is an event "PostDeployment" that will be invoked whenever a deployment is complete with the result of that deployment.
Microsoft ASP.NET WebHooks preview provides a common model for receiving and processing WebHooks from any number of WebHook providers includign support for Kudu (Azure Web App Deployment).
So you may use Kudu WebHooks to get notified when an update has been deployed. (But that will require using Git Deploy instead of other ways to publish your web site).
Here is the way to do it :
First Install the Microsoft.AspNet.WebHooks.Receivers.Azure Nuget package.
The, Add these two lines to the WebApiConfig.Register method:
config.InitializeReceiveKuduWebHooks();
config.InitializeReceiveAzureAlertWebHooks();
Then Add a handler :
public class KuduWebHookHandler : WebHookHandler
{
public KuduWebHookHandler()
{
Receiver = "kudu";
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
// Convert to POCO type
KuduNotification notification = context.GetDataOrDefault<KuduNotification>();
// Get the notification message
string message = notification.Message;
// Get the notification author
string author = notification.Author;
return Task.FromResult(true);
}
}
Then configure a secret that can validates that the WebHook requests indeed come from Kudu.
Use high-entropy values such as a SHA256 hash or similar, which you can get from http://www.freeformatter.com/hmac-generator.html. Also, set them through the Azure Portal instead of hard-coding them in the Web.config file
Also, set them through the Azure Portal instead of hard-coding them in the Web.config file.
There is a more complete Post on the subject here (http://blogs.msdn.com/b/webdev/archive/2015/10/04/receive-webhooks-from-azure-alerts-and-kudu-azure-web-app-deployment.aspx)
Hope this helps
Best regards
Stéphane
I figured it out. In the Application_Start event I bind
RoleEnvironment.Stopping += RoleEnvironmentStopping;
private void RoleEnvironmentStopping(object sender, RoleEnvironmentStoppingEventArgs e)
{
// do something ...
}

Deploying an HttpHandler web service

I am trying to build a webservice that manipulates http requests POST and GET.
Here is a sample:
public class CodebookHttpHandler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.HttpMethod == "POST")
{
//DoHttpPostLogic();
}
else if (context.Request.HttpMethod == "GET")
{
//DoHttpGetLogic();
}
}
...
public void DoHttpPostLogic()
{
...
}
public void DoHttpGetLogic()
{
...
}
I need to deploy this but I am struggling how to start. Most online references show making a website, but really, all I want to do is respond when an HttpPost is sent. I don't know what to put in the website, just want that code to run.
Edit:
I am following this site as its exactly what I'm trying to do.
I have the website set up, I have the code for the handler in a .cs file, i have edited the web.config to add the handler for the file extension I need. Now I am at the step 3 where you tell IIS about this extension and map it to ASP.NET. Also I am using IIS 7 so interface is slightly different than the screenshots. This is the problem I have:
1) Go to website
2) Go to handler mappings
3) Go Add Script Map
4) request path - put the extension I want to handle
5) Executable- it seems i am told to set aspnet_isapi.dll here. Maybe this is incorrect?
6) Give name
7) Hit OK button:
Add Script Map
Do you want to allow this ISAPI extension? Click "Yes" to add the extension with an "Allowed" entry to the ISAPI and CGI Restrictions list or to update an existing extension entry to "Allowed" in the ISAPI and CGI Restrictions list.
Yes No Cancel
8) Hit Yes
Add Script Map
The specified module required by this handler is not in the modules list. If you are adding a script map handler mapping, the IsapiModule or the CgiModule must be in the modules list.
OK
edit 2: Have just figured out that that managed handler had something to do with handlers witten in managed code, script map was to help configuring an executable and module mapping to work with http Modules. So I should be using option 1 - Add Managed Handler.
I know what my request path is for the file extension... and I know name (can call it whatever I like), so it must be the Type field I am struggling with. In the applications folder (in IIS) so far I just have the MyHandler.cs and web.config (Of course also a file with the extension I am trying to create the handler for!)
edit3: progress
So now I have the code and the web.config set up I test to see If I can browse to the filename.CustomExtension file:
HTTP Error 404.3 - Not Found
The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.
So in IIS7 I go to Handler Mappings and add it in. See this MSDN example, it is exactly what I am trying to follow
The class looks like this:
using System.Web;
namespace HandlerAttempt2
{
public class MyHandler : IHttpHandler
{
public MyHandler()
{
//TODO: Add constructor logic here
}
public void ProcessRequest(HttpContext context)
{
var objResponse = context.Response;
objResponse.Write("<html><body><h1>It just worked");
objResponse.Write("</body></html>");
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
I add the Handler in as follows:
Request path: *.whatever
Type: MyHandler (class name - this appears correct as per example!)
Name: whatever
Try to browse to the custom file again (this is in app pool as Integrated):
HTTP Error 500.21 - Internal Server Error
Handler "whatever" has a bad module "ManagedPipelineHandler" in its module list
Try to browse to the custom file again (this is in app pool as CLASSIC):
HTTP Error 404.17 - Not Found
The requested content appears to be script and will not be served by the static file handler.
Direct Questions
1) Does the website need to be in CLASSIC or INTEGRATED mode? I don't find any reference of this in the online material, whether it should be either.
2) Do I have to compile the MyHandler.cs to a .dll, or can I just leave it as .cs? Does it need to be in a bin folder, or just anywhere in root?
RE your questions:
I don't know the answer to the first one (CLASSIC or INTEGRATED); but I can help with the second...
Yes you'll need to compile it first. I have never tried deploying dll's to anywhere other than the bin, given that that's the standard I would be suspect in putting them anywhere else even if it did work.
The way I deploy HttpHandlers is quiet straight forward - all the hard work's done in web.config, I'v enever had to go into IIS to change any settings.
For a start, for the http request to be handled by ASP.NET you need to use a request suffix that's already piped to ASP.NET - like .aspx or ashx. If you want to use something else you will need to config IIS to do this, as per your managed handler img above.
I tend to use .ashx e.g: http://localhost/foo/my/httphandler/does/this.ashx
All you need to do (assuming you've compiled athe HttpHandler into a DLL and deployed it to the site) is add the necessary config.
<configuration>
<system.web>
<httpHandlers>
<add verb="*"
path="*.ashx"
type="MyApp.PublishingSystem.HttpHandlers.GroovyHandler, MyApp.PublishingSystem" />
</httpHandlers>
</system.web>
</configuration>
Obviously (?) you can change / restrict the scope using the path, e.g:
path="*.ashx"
path="*ListWidgets.ashx"
path="*Admin/ListWidgets.ashx"
More info here: http://msdn.microsoft.com/en-us/library/ms820032.aspx
An important gotcha to look out for is the order in which you declare your HttpHandlers in the config; from what I remember ones declared first take precedent. So in this example...
<add verb="*" path="*foo.ashx" type="MyApp.PublishingSystem.HttpHandlers.FooHandler, MyApp.PublishingSystem" />
<add verb="*" path="*.ashx" type="MyApp.PublishingSystem.HttpHandlers.GroovyHandler, MyApp.PublishingSystem" />
...the groovy handler will handle all HttpRequests except any that end in foo.ashx
By the way, I make use of HttpHanldrs in my open source .net CMS / app framework, you might find some helpful code there (?): http://morphfolia.codeplex.com/
Make sure the app pool's .NET Framework Version is set correctly...
I deployed a .NET 4.0 web app on a .NET 2.0 app pool and got this error. Set the app pool to v4.X and the ashx was served like a champ.

Categories