I would like to make a RESTful app of HTTPhandlers without having to define every endpoint by making an entry in the web.config, i'd like the style of attaching attributes to a class constructor eg:
public class obj : IHttpHandler
{
[WebGet(UriTemplate = "/accounts/{id}")]
public obj(string id)
{
// this is just an eg, it worild normally include caching and
// a template system
String html = File.ReadAllText("/accounts/accounts.htm");
html.replace("id", id);
httpcontext.current.response.write(html)
}
}
instead of
<httpHandlers>
<clear />
<add verb="GET" path="/accounts/*" type="MyApp.obj" />
</httphandlers>
The way i'm doing it now i have 100's of endpoints in the web.config :( i'd rather define them in the class. And i don't want to make extra files (.asmx) either. I'd like an app of just .htm files with tokens and .cs files
Thanks!
You could automate the registration of the endpoints and so on, with a custom ServiceHost, which overrides the ApplyConfiguration() method, which then virtualizes the configuration so that it does not have to be in the web.config file.
Here's a starting point. It doesn't do exactly what you want, but it illustrates the concept of virtualizing the configuration.
Related
There are a lot of examples of configuring sinks and their properties using app settings configuration. However, I can't really wrap my head around configuring a custom enricher via app settings. Can this be done? I've tried to specify the configuration using the full path to my class and assembly's name, but it doesn't seem to work. Here's an example of the configuration I've tried to use:
<add key="serilog:enrich:with" value="MyApp.Logging.Serilog.MyEnricher, MyApp" />
The key-value pair syntax currently needs an extension method defined for this case to work, e.g.:
static class MyLoggerEnrichmentConfigurationExtensions
{
public static LoggerConfiguration WithMyEnricher(this LoggerEnrichmentConfiguration enrich)
{
return enrich.With(new MyEnricher());
}
}
It's then referenced and called like so:
<add key="serilog:using:MyApp" value="MyApp" />
<add key="serilog:enrich:WithMyEnricher" />
My WCF service is locally hosted like this:
localhost/service.svc
On the server it's hosted like this:
servername/extra/service.svc
When making a request SwaggerWcf uses the base path and adds the configered elements:
localhost/user/parameter
But it should be:
localhost/extra/user/parameter
So I need WCF to not just use the hostname, but also a little more of the url.
I could add it to the SwaggerWcf configuration, which is user and could be extra/user. But then it won't work locally anymore.
I've tried adding a Config class that could read configuration attributes statically:
using System.Configuration;
namespace Project
{
public static class Config
{
public static readonly string SwaggerWcfRequestPath = ConfigurationManager.AppSettings["SwaggerWcf.RequestPath"] ?? "/user";
}
}
But when I try to use this:
[SwaggerWcf(Config.SwaggerWcfRequestPath)]
public class Service: IService
It still produces the same error:
Error 46 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
How can this be done dynamically? Then it can be different for different environments.
Currently there is no way to do it
But I'll take a look into this later today
Probably the best way is allowing to override it using Web.config
<configSections>
<section name="swaggerwcf" type="SwaggerWcf.Configuration.SwaggerWcfSection, SwaggerWcf"/>
</configSections>
<swaggerwcf>
<settings>
<setting name="Host" value="www.msampleservice.com"/>
<setting name="BasePath" value="/myserviceapi"/>
</settings>
</swaggerwcf>
I have a few http handlers (IHttpHandler) in my asp.net web project. Now I want to restrict access to these handlers. For Handler1 I want to allow only POST requests, and for Handler2 I want to allow only GET requests.
In my web.config I modified the <httpHandlers> section as shown below, but both handlers still process all verb types. Is there something I've missed? I'm testing it using IIS Express.
<httpHandlers>
<add verb="POST" path="Handler1.ashx" type="MyNamesapce.Handler1, MyAssembly"/>
<add verb="GET" path="Handler2.ashx" type="MyNamesapce.Handler2, MyAssembly"/>
</httpHandlers>
The reason this isn't working for you is that you've conflated two slightly different "flavours" of something that implements IHttpHandler.
There are two ways that you can implement an IHttpHandler with asp.net:
Create a class that implements IHttpHandler, e.g. MyCustomHandler.cs. This type of handler won't respond to any requests without being configured in your web.config file.
Create an .ashx file (which it looks like you've done), e.g. MyOtherHandler.ashx. This type of handler will respond to any requests to its URL, e.g. http://localhost/MyOtherHandler.ashx
The first type requires entries in the web.config file to work, the second doesn't. This is why you're seeing your .ashx handlers responding to all HTTP verbs, because they're being handled by the part of the asp.net framework that responds to requests for .ashx files, rather than being triggered by your web.config file. If you're using IIS Express, you can see this configured in the file %USERPROFILE%\Documents\IISExpress\config\applicationhost.config. Search for ".ashx" and you'll find a line similar to the below in the <system.webServer><handlers> section:
<add name="SimpleHandlerFactory-Integrated-4.0" path="*.ashx"
verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory"
preCondition="integratedMode,runtimeVersionv4.0" />
This is equivalent to what you've been adding to your web.config, but is responsible for telling IIS/asp.net "respond to any URLs that end in .ashx with any of the listed verbs by having the code in the type System.Web.UI.SimpleHandlerFactory deal with it. This code then loads your .ashx file.
To create a handler that can respond to any address you choose, you need (in short) a .cs file containing something similar to:
using System.Web;
namespace HttpHandlers
{
public class Handler4 : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello World from Handler4.cs");
}
}
}
You can then wire it into your web.config file with:
<add name="Handler4" verb="POST" path="Handler4.ashx" type="HttpHandlers.Handler4, HttpHandlers" />
NOTE: My project that I created to test this is called "HttpHandlers", hence the type declaration that I've specified in that web.config snippet.
I'm looking for a walkthrough on how to create and use a custom provider for ASP.Net Healthmonitoring.
So far I've only worked with the e-mail provider that generates e-mails on errors. Basically I want to do the same, but with more flexibility:
I want to use the HealthMonitoring features (I don't want to use the Application_OnError event in the global.asax) in a way that allows me have access to an event, that gets thrown like "OnNewHealthMonitoringEntry" with all the information provided in the e-mail, to run custom code.
Edit:
Based on the source code provided here http://www.asp.net/general/videos/how-do-i-create-a-custom-provider-for-logging-health-monitoring-events I was able to build my own custom provider and implement it. Now I want to add some new attributes to configure my custom provider.
Here is what the web.config looks like:
<healthMonitoring>
<bufferModes>
<add name="Log Notification" maxBufferSize="1" maxFlushSize="1" urgentFlushThreshold="1" regularFlushInterval="Infinite" urgentFlushInterval="00:00:10"/>
</bufferModes>
<providers>
<add name="FileEventProvider" buffer="true" bufferMode="Log Notification" type="healthmonitoringtest.FileHealthMonitorEventProvider"/>
</providers>
<profiles>
<add name="Custom" minInstances="1" maxLimit="Infinite" minInterval="00:00:00"/>
</profiles>
<rules>
<add name="File Event Provider" eventName="All Errors" provider="FileEventProvider" profile="Custom"/>
</rules>
</healthMonitoring>
If I attempt to add an attribute to the provider, like this
<providers>
<add name="FileEventProvider" buffer="true" bufferMode="Log Notification" foo="bar" type="healthmonitoringtest.FileHealthMonitorEventProvider"/>
</providers>
I'll get an error saying:
An exception of type
'System.Configuration.ConfigurationErrorsException'
occurred in System.Web.dll but was not
handled in user code Additional
information: Unexpected attribute foo
in the configuration of the
FileEventProvider.
Is it possible to store configuration necessary for custom provider close to the healthMonitoring section? I guess I could include the settings into the appSettings node, but I'd like to configure it somehow with attributes (inside the healthMonitoring node). Is that possible?
Edit2:
You might take a look at this article: http://www.tomot.de/en-us/article/6/asp.net/how-to-create-a-custom-healthmonitoring-provider-that-sends-e-mails
The following series of articles will take you through the basics of using the Health Monitoring System upto creating Custom Events.
Then the following 26 minute video will take you through creating a custom provider that records events to a text-based log file.
UPDATE Based on Comment
Looking at your update and using Reflector to look at the source for the BufferedWebEventProvider class that you base your custom provider on, I have found that the Initialize method in BufferedWebEventProvider does a check at the end to see if there are any attributes that it doesn't recognize. This is done by removing values from the config NameValueCollection parameter as soon as they are assigned to the properties or fields of the BufferedWebEventProvider. Then a check is done to see if the config parameter is empty and if not that means that there are extra attributes added, which causes an exception to be thrown.
As to how to fix this problem, one option is to:
Move the call to base.Initialize to the end of the method
Remove the additional attributes as soon as you assign them to variables just like the provider does.
Something like the following would work:
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
foo = config["foo"];
if (String.IsNullOrEmpty(foo))
{
// You can set a default value for foo
}
//remove foo from the config just like BufferedWebEventProvider with the other
//attributes. Note that it doesn't matter if someone didn't proivde a foo attribute
//because the NameValueCollection remains unchanged if you call its Remove method
//and the name doesn't exist.
config.Remove("foo");
base.Initialize(name, config);
}
Hopefully this works out for you.
I'm trying to unit test values that will eventually wind up in a web.config file. In my test project, I created an app.config file with a web.config section to hold the settings. In a normal situation, I would call System.Configuration.ConfigurationSettings.AppSettings, but in this case, that doesn't work. I saw this question, which is very similar, but doesn't address how to get the NameValueCollection out of the config file. Here is an example of the config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear/>
<add
name="CustomMembershipProvider"
applicationName="SettlementInfo"
enablePasswordRetrieval="false"
enablePasswordReset="false"
requiresQuestionAndAnswer="true"
writeExceptionsToEventLog="true" />
</providers>
</membership>
</system.web>
</configuration>
Has anyone dealt with this before?
I guess I'm confused here; it looks like you're trying to test that ASP.NET is using your custom membership provider appropriately. Correct?
If so, I'm 99.999% sure that you cannot unit test this using the MS framework; you must integration test it by deploying it to the webserver (or running Cassini in VS) and typing a username/password into your login page.
Now, it's possible I've misunderstood your request. If so, let me know and I'll edit my answer accordingly.
Edit:
For right now, I'm really just trying
to test the NameValue pairs coming out
of the config file, to make sure that
if the values aren't present, my
defaults are being applied. In other
words, I want to try to pull
applicationName, and verify that it
equals "SettlementInfo", and so on.
After that, I will be using
integration testing to ensure that
ASP.NET is using the custom framework
in place of the default one. Does that
make sense?
I need more than a comment to reply, so I'm editing. If I read you correctly, you are wanting to unit test your program to ensure that it deals with configuration correctly, yes? Meaning you want to ensure that your code grabs, for example, the correct AppSettings key and handles a null value therein, correct?
If that's the case, you're in luck; you don't need an app.config or web.config at all, you can set the values you need as part of your test setup.
For example:
[TestMethod]
public void Test_Configuration_Used_Correctly()
{
ConfigurationManager.AppSettings["MyConfigName"] = "MyConfigValue";
MyClass testObject = new MyClass();
testObject.ConfigurationHandler();
Assert.AreEqual(testObject.ConfigurationItemOrDefault, "MyConfigValue");
}
[TestMethod]
public void Test_Configuration_Defaults_Used_Correctly()
{
// you don't need to set AppSettings for a non-existent value...
// ConfigurationManager.AppSettings["MyConfigName"] = "MyConfigValue";
MyClass testObject = new MyClass();
testObject.ConfigurationHandler();
Assert.AreEqual(testObject.ConfigurationItemOrDefault, "MyConfigDefaultValue");
}
I believe you only have access to the webconfig file while your application is actually beeing started up. The solution is rather easy -> "Fake" your config. Use a NameValueCollection and use that instead:
private static NameValueCollection CreateConfig()
{
NameValueCollection config = new NameValueCollection();
config.Add("applicationName", "TestApp");
config.Add("enablePasswordReset", "false");
config.Add("enablePasswordRetrieval", "true");
config.Add("maxInvalidPasswordAttempts", "5");
config.Add("minRequiredNonalphanumericCharacters", "2");
config.Add("minRequiredPasswordLength", "6");
config.Add("requiresQuestionAndAnswer", "true");
config.Add("requiresUniqueEmail", "true");
config.Add("passwordAttemptWindow", "10");
return config;
}
Now you could easily pass that collection into your class that parses data from it.
You should be able to use the ConfigurationManager.GetSection() method to pull out whatever you want.
Actually, if you are using NUnit, you can stick that in an App.config in your test project.
Then add this line to your Post-build event:
copy /Y “$(ProjectDir)App.config” “$(TargetDir)$(TargetFileName).config”
When you create the new provider in your tests, NUnit will pass the values in your app.config to the provider in the initialize method.
Why not just stick it in the web.config file?