I want to replace an old ISAPI filter that ran on IIS6. This filter checks if the request is of a special kind, then manipulates the header and continues with the request. Two headers are added in the manipulating method that I need for calling another special ISAPI module.
So I have ISAPI C++ code like:
DWORD OnPreProc(HTTP_FILTER_CONTEXT *pfc, HTTP_FILTER_PREPROC_HEADERS *pHeaders)
{
if (ManipulateHeaderInSomeWay(pfc, pHeaders))
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
return SF_STATUS_REQ_FINISHED;
}
I now want to rewrite this ISAPI filter as a managed module for the IIS7. So I have something like this:
private void OnMapRequestHandler(HttpContext context)
{
ManipulateHeaderInSomeWay(context);
}
And now what? The request seems not to do what it should?
I already wrote an IIS7 native module that implements the same method. But this method has a return value with which I can tell what to do next:
REQUEST_NOTIFICATION_STATUS CMyModule::OnMapRequestHandler(IN IHttpContext *pHttpContext, OUT IMapHandlerProvider *pProvider)
{
if (DoSomething(pHttpContext))
{
return RQ_NOTIFICATION_CONTINUE;
}
return RQ_NOTIFICATION_FINISH_REQUEST;
}
So is there a way to send my manipulated context again?
I finally found it. As I stated in the comments I add two headers to the request that are needed by my DLL that finally handles the request. The url header contains the path to the DLL. So I have to do a redirect to that DLL.
This is done with the following code:
private void OnMapRequestHandler(HttpContext context)
{
ManipulateHeaderInSomeWay(context);
string url = context.Request.Header["url"]; // path of the DLL
// now this is the important call!
context.Server.TransferRequest(url, true);
}
Related
I have a classic client application with API calls to the server to make operations on DB. But for some reason, every client method in service makes two calls to my controller when I need just once.
What's the reason, why and how I could fix it? Moreover, the second call comes also if the back-end didn't return anything yet but still performing operation.
That's some code example:
Method calls the service:
export class TestingComponent implements OnInit {
results: any;
constructor(
private testingservice: TestingService) { }
ngOnInit() {
let test = true;
this.startingMethod(test);
}
startingMethod(test) {
this.testingservice.getData(test).subscribe( data => {
this.results = data;
})};
}
Service method:
export class TestingService{
constructor(private configService: ConfigService, private http: HttpClient) { }
getData(test: boolean): Observable<any> {
let urlc = this.configService.getCurrentEndpoints();
let params = new HttpParams();
params = params.set('test', test);
return this.http.get<any>(`${urlc.baseUrl}${urlc.getdata}`, {'params': params });
}
}
I hope was clear enough, but if I don't freely ask me more details. Thanks a lot!
Seems like it's a bug from the browsers, they sends the second request to get the favicon of the page, and since they don't have it, it just brings anything.
This is a link for the Chrome bug.
https://bugs.chromium.org/p/chromium/issues/detail?id=39402
Firefox, and most other browsers, also send out a request for a favicon when they first connect, but cache the result i.e. if there isn't a favicon returned first time, they don't keep trying - which is why you're only seeing a single request from Firefox. It seems Chrome is unfortunately a little too persistent with its favicon requestiness.
There may be two reasons for this case:
As you mentioned that there are two calls for BE, maybe one of them is preflight request.
You can read about this here: https://livebook.manning.com/book/cors-in-action/chapter-4/
The second reason may be multiple subscriptions:
You can change the calling of service call like:
startingMethod(test) {
this.testingservice.getData(test).toPromise().then( data => {
this.results = data;
})}
or you can use a subscription object like:
subscription = new Subscription();
startingMethod(test) {
this.subscription.add(
this.testingservice.getData(test).subscribe( data => {
this.results = data;
}));
}
ngOnDestroy(){
this.subscription.unsubscribe();
}
Moreover, the second call comes also if the back-end didn't return anything yet but still performing the operation.
I suspect that you are refering to the OPTIONS request. That is a CORS preflight request that is generated by the browser itself which is totally normal.
OPTIONS requests are what we call pre-flight requests in Cross-origin resource sharing (CORS).
They are necessary when you're making requests across different origins in specific situations.
I am trying to create a WebHookHandler for Webhooks send from WordPress WooCommerce in ASP.NET C#.
I started with creating a ASP.NET C# Azure API App WebApplication Project and adding the relevant references (Microsoft.AspNet.WebHooks.Common, Microsoft.AspNet.WebHooks.Receivers, Microsoft.AspNet.WebHooks.Receivers.WordPress). Added the WebHookConfig, WordPressWebHookHandler and registered the WebHookConfig in the GlobalAsax.
I then published the application as an Azure App Service.
My WordPressWebHookHandler is still the default of the examples and looks like this:
public class WordPressWebHookHandler : WebHookHandler
{
public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)
{
// make sure we're only processing the intended type of hook
if("WordPress".Equals(receiver, System.StringComparison.CurrentCultureIgnoreCase))
{
// todo: replace this placeholder functionality with your own code
string action = context.Actions.First();
JObject incoming = context.GetDataOrDefault<JObject>();
}
return Task.FromResult(true);
}
}
When testing a User Creation WebHook in WooCommerce I can see the request in the log as below.
But unfortunately it is never received while debugging and I see below error.
I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook. Or possibly it is handled wrong in the routing and ends up in another controller.
Any help is much appreciated.
Your WebHookReceiver is wrong
There is a mismatch of expecting HTML Form Data, when in fact it should be expecting JSON.
WordPressWebHookHandler is still the default
This is what is causing your error. If you look at the WordPressWebHookReceiver, the ReceiveAsync() method implementation, calls out to ReadAsFormDataAsync() method, which is not what you want, as your Content-Type is json. So, you want to be doing ReadAsJsonAsync().
Solution: Don't use the WordPressWebHookReceiver and switch it to another one that will call ReadAsJsonAsync().
Looking at the code
I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook.
You had the right idea, so I dug up some of the code to explain exactly why this was happening.
The code block below is the ReceiveAsync() method that is overridden in the WordPressWebHookReceiver. You can see that it is calling the ReadAsFormDataAsync() which is not what you want...
public override async Task<HttpResponseMessage> ReceiveAsync(
string id, HttpRequestContext context, HttpRequestMessage request)
{
...
if (request.Method == HttpMethod.Post)
{
// here is what you don't want to be called
// you want ReadAsJsonAsync(), In short, USE A DIFFERENT RECEIVER.
NameValueCollection data = await ReadAsFormDataAsync(request);
...
}
else
{
return CreateBadMethodResponse(request);
}
}
A quick search through the repository for classes that call the ReadAsJsonAsync() method, shows that the following recievers implement it:
DynamicsCrmWebHookReceiver
ZendeskWebHookReceiver
AzureAlertWebHookReceiver
KuduWebHookReceiver
MyGetWebHookReceiver
VstsWebHookReceiver
BitbucketWebHookReceiver
CustomWebHookReceiver
DropboxWebHookReceiver
GitHubWebHookReceiver
PaypalWebHookReceiver
StripeWebHookReceiver
PusherWebHookReceiver
I assumed that the CustomWebHookReceiver would fit your requirements, so can grab the NuGet here. Otherwise you can implement your own, or derive it from this class, etc.
Configuring a WebHook Recevier
(Copied from the Microsoft Documentation)
Microsoft.AspNet.WebHooks.Receivers.Custom provides support for
receiving WebHooks generated by ASP.NET WebHooks
Out of the box you can find support for Dropbox, GitHub, MailChimp,
PayPal, Pusher, Salesforce, Slack, Stripe, Trello, and WordPress but
it is possible to support any number of other providers
Initializing a WebHook Receiver
WebHook Receivers are initialized by registering them, typically in
the WebApiConfig static class, for example:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
// Load receivers
config.InitializeReceiveGitHubWebHooks();
}
}
There is a problem with the data format that you send in your request. You must use format of HTML Form as your error message said.
Proper POST data format is described here: How are parameters sent in an HTTP POST request?
Don't forget to set Content-Length header and correct Content-Type if your library doesn't do it. Usually the content type is application/x-www-form-urlencoded.
I would like to make some additions to Svek's answer as I now got my Proof-of-concept completed and understand a bit more about the receivers.
His answer pointed me in the right direction, but needs a little addition.
WordpressWebHookReceiver
Can take in Wordpress Webhooks of type HttpPost. This does not work with Woocommerce as Woocommerce sends Json Webhook messages and will fail the HttpPost validation which is build into the WordpressWebHookReceiver class.
CustomWebHookReceiver
Can take in custom ASP.NET Webhooks. The custom ASP.NET webhooks have a specific partner for validation which includes but is not limited to the 'ms-signature'. Even adding the header will not suffice as the signature is also used in a different way from out of the box Woocommerce to encrypt the message. Basically coming to a point that you can't integrate Woocommerce with the CustomWebHookReceiver without changing the Webhook classes of Woocommerce.
GenericWebHookReceiver
This is the receiver you want, which accepts basically a generic set of Json data and will be able to use the "code" query parameter to verify the secret which you can add in the web.config of your asp.net api application. I used this receiver to finish the Proof-of-concept and got both the signature validation as well as the deciphering of the message working right of the bat.
My basic class which I will start to build into a real solution can be viewed below and changes the JObject into a dynamic object in the methods I call from the class. As you can see I have two methods currently added, one for the customer create and one for the order create to call the respective methods which do an insert into Dynamics 365 (former CRM).
public class GenericJsonWebHookHandler : WebHookHandler
{
public GenericJsonWebHookHandler()
{
this.Receiver = "genericjson";
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
var result = false;
try
{
// Get JSON from WebHook
var data = context.GetDataOrDefault<JObject>();
if(context.Id != "crcu" && context.Id != "cror")
return Task.FromResult(true);
if (context.Id == "crcu")
{
result = WoocommerceCRMIntegrations.Entities.Contact.CreateContactInCRM(data);
}
else if (context.Id == "cror")
{
result = WoocommerceCRMIntegrations.Entities.Order.CreateOrderInCRM(data);
}
}
catch (Exception ex)
{
result = false;
}
return Task.FromResult(result);
}
}
I have written a WCF API. It runs (ran) just fine.
Now that I need to open it up to public access I tried to implement API Key verification following the example of Ron Jacobs and the accompanying video on endpoint.tv
This basically just uses a list of GUIDs stored in an xml file that represent the valid API keys. The authorization request key is passed via query string and validated in a custom ServiceAuthorizationManager like this:
protected override bool CheckAccessCore(OperationContext operationContext)
{
return IsValidAPIKey(operationContext);
}
public static bool IsValidAPIKey(OperationContext operationContext)
{
string key = TvinQuery.GetAPIKey(operationContext);
Guid apiKey;
// Convert the string into a Guid and validate it
if (Guid.TryParse(key, out apiKey) && TvinQuery.APIKeys.Contains(apiKey))
{
return true;
}
else
{
return false;
}
}
TvinQuery.APIKeysis a simple List<string> containing the valid guids from the xml file.
The solution compiles, but when I publish it on my server and try to access the service there, I get a FileNotFound exception for file or assembly "WCFWebHttp" or one of its dependencies.
The cause for this is very obviously this part in the service behavior node of web.config:
<serviceAuthorization serviceAuthorizationManagerType="WCFWebHttp.APIKeyAuthorization, WCFWebHttp" />
Unfortunately, neither a search through my assemblies nor through my file system nor an internet search nor a nuget package search came up with an assembly of that name.
Event viewer and enabled tracing did not reveal any further information either.
What is it? How can I solve this error? Is there anything wrong or missing with the example? It worked just fine in the video and that was a live presentation. :-?
As from this picture:
at https://blogs.msdn.microsoft.com/rjacobs/2010/06/14/how-to-do-api-key-verification-for-rest-services-in-net-4/
it looks like WCFWebHttp is an assembly implemented in the source code of the sample you've used (which I can't download right now to make sure because of server error).
I'm trying to configure routing in my application in such way: paths to files should work for every directory except one special. And access to files in this direcotory should be processed by my controller action. If I write smth like this:
routes.Map("Specialfolder/{name}", "Controller", "Action");
it works only for unexisting files. Controller doesn't catches route for existing file
If I add this line:
routes.RouteExistingFiles = true;
Working with files in my folder is ok. But files in other directories aren't routed anymore. How to fix this?
Here is a sample ActionFilter sample code snippet that you can use
public class UriActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
// Sample: somehow identify the user, in case of a custom identity, replace the below line to get the user identifier
var user = System.Threading.Thread.CurrentPrincipal.Identity.Name;
// get the parameter that will let you know what is the image or path that is being requested now
var ctxParams = filterContext.ActionParameters;
// if the user does not have the permission to view, return 403
filterContext.RequestContext.HttpContext.Response.StatusCode = 403;
return;
}
base.OnActionExecuting(filterContext);
}
}
In this snippet, you have to replace with the corresponding service calls etc..
W.R.TO the OnAuthorization, I was mentioning that you can use so that any one that gets access to the image may hit the URI directly and that can be used filtered. We are using that way to restrict the URI access directly by a given user.
This will help you to override the MVC authorization of each request that the controller gets. This is a more of a restriction by URI alone. However, your case can be satisfied by the above action filter too as parsing of the uri parameters can be quite tricky at times.
I need to rewrite the URL using ASP.NET with code behind as C#. My Application contains the following URL...
http://www.mywebsite.com/Products.aspx?id=1&pg=1
However, I need to rewrite the URL in such a way that the user gets the same contents of the above URL when the user types the following URL...
http://www.mywebsite.com/CategoryName/ProductName/1
Can any of you guys help me with the complete necessary code how to do it?
I mean the web.config, Global.asax, etc...
If you have IIS7, the best option would be to use IIS Url Rewrite Module.
There might be a couple of options I could suggest:
One
You could look at setting up an HttpHandlerFactory.
I have written one myself: http://mvcsnippets.blogspot.co.uk/2012/10/custom-cms-using-httphandlerfactory.html
here is the basic gist:
namespace Web.Helpers {
public class HttpCMSHandlerFactory : IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, string requestType, string url,string pathTranslated)
{
string pageName = Path.GetFileNameWithoutExtension(context.Request.PhysicalPath);
//on Server.Transfer the context is kept, so this just overrides the existing value.
if (context.Items.Contains("PageName"))
{
context.Items["PageName"] = pageName; } else { context.Items.Add("PageName", pageName); }
FileInfo fi = new FileInfo(context.Request.MapPath(context.Request.CurrentExecutionFilePath));
//if File is not physical
if (fi.Exists == false)
{
//return page to CMS handler the context containing "PageName" is passed on to this page, which then calls to the database to return the copy.
return PageParser.GetCompiledPageInstance(url, context.Server.MapPath("~/CMSPage.aspx"), context);
}
else
{
// Returns real page.
return PageParser.GetCompiledPageInstance(context.Request.CurrentExecutionFilePath, fi.FullName, context);
}
}
}
The behaviour I was trying to handle was that if there might be CMS content and I didnt want to have to create a page each time I needed information served, but if a physical page exists that should be returned.
For you, you might want to accept the URL, break it down to the component parts.
so http://www.mywebsite.com/CategoryName/ProductName/1 becomes:
context.Items["Categoryname"] = valueFromUrlasCategoryName;
context.Items["Productname"] = valueFromUrlasProductName;
context.Items["Id"] = valueFromUrlasId (or pg);
return PageParser.GetCompiledPageInstance(url, context.Server.MapPath("~/ControlPage.aspx"), context);
Then on your control page you can intercept these values from the context passed in and interrogate the data as needed.
In the web.config you point a reference towards your HttpHandlerFactory
<httphandlers>
<add path="*.aspx" type="Web.Helpers.HttpCMSHandlerFactory, Web.helpers" verb="*"/>
</httphandlers>
In your case you could set the path as "." to capture all traffic. this would mean you would have to add handling for images and scripts.
you will also need to make sure you add a wildcard to your IIS for extensionless pages.
There are plenty of articles about HttpHandlerFactories on the web, and might explain better.
Two
The sort of thing you are after is part of MVC, could you look at changing your UI to use that?