I've build a RESTful web service using NancyFX which I'm now trying to POST to from a separate domain. Of course when I do that, I see a failed OPTIONS message in the console because this is a Cross Site POST and I need to ensure that Nancy responds correctly to the OPTIONS message being sent by the browser. However, when I define a route in my Nancy module:
this.Options["/options/"] = _ => this.OptionsRequest();
private dynamic OptionsRequest()
{
return this.Response.AsJson(Request)
.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
}
The code never gets hit. I can set a breakpoint on the OptionsRequest() method in debugger and see that the code is never getting hit. However I can issue an OPTIONS request in postman, and the server returns a response (interestingly, it seems to return a response from all URIs, not just the /options/ route I've defined).
Is there a default Nancy OPTIONS behaviour I have to override in order to specify routes for OPTIONS, or is this something to do with the service being hosted in the Visual Studio Development Web Server (Casini)? I've tried everything I can think of and I'm still stumped as to why I can't define behaviour for this particular verb.
Turns out this was a bug in Nancy v0.17.1 (see https://github.com/NancyFx/Nancy/pull/1093). Upgrading to 0.18.0 fixed the issue and allowed my OPTIONS route to work correctly.
Related
What I have:
A simple Web API - POST api/examples
Postman to send the request.
When I make the request with the content type set as application/json everything works just fine. But when I change to anything else I get the following response:
{
"": [
"The input was not valid."
]
}
From Kestrel logs I get:
the application completed without reading the entire request body.
This is how the web api is handling invalid requests. What I want to find out is how can I capture and handle this kind of exception and change the default message.
I do have a error handling middleware, but in this scenario the request is invalid, so it's never called.
How can I change this default behavior?
Well, after some while, I had to come back to this issue.
I've find out that this validation has to do with the automatic model state validation of the ASP.NET Core Framework. So everytime a invalid value is passed to the web API controller, a filter, the ModelStateInvalidFilter, is executed prior and ends up firing the 400 bad request: "The input was not valid."
Although there are some ways to override this behavior, for my case, I found it best to just disable it. To do so just add the following lines to your Startup.cs in the ConfigureServices method:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
More details about this and how to override it, you can check here:
http://www.talkingdotnet.com/disable-automatic-model-state-validation-in-asp-net-core-2-1/
and here:
Correct way to disable model validation in ASP.Net Core 2 MVC
This might be a silly question but here is my simple webapi 2 method
public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
Now if I run it , on my pc it runs on http://localhost:3145/Products, and I can see the products as XML
It also works using soapui
But now if I try to access this with a html file and this javascript
<script type="text/javascript">
function GetProducts() {
$.ajax({
url: "http://localhost:3145/Products",
dataType: "json",
success: function (data) {
for (var i = 0; i < data.length; i++)
$('#myDiv').append(data[i].Category).append("<br/>");
},
error: function (xhr, status) {
alert(xhr);
}
});
}
</script>
</head>
<body onload="GetProducts()">
<h1>My App</h1>
<div id="myDiv"></div>
I get the CORS error
How is SOAP UI not getting an error here when it is using http as well?
Thanks
Put that index file into some server location, and then browse the page with server url , like, http://localhost/virtual_dir/index.html , else it will say it file:/// domain does not match with http://localhost:port . And you might face CORS issue if you deploy this page to some other domain and start using.
I have seen that you are using webapi , and you might face CORS issue if you place you JS in domain ( "example1.com"), i mean files served from example1.com will have ajax calls to webapi and that webapi may be hosted in example2.com. This will raise CORS issue. Browser restricts ajax call to other domains, unless that domain allow you to invoke. To achieve this, you can follow this link - Angular.js $resource with ASP.Net webapi? (don't go by the title)
I have answered there the same scenario.
I just experienced the same situation with a web page I was developing that needed to send a SOAP request to a web service that was served by a remote server that was not CORS-enabled. This was in a test environment; in production, the web page and web service are served from the same domain and port.
How is SOAP UI not getting an error here when it is using http as well?
Yes, SoapUI and a web browser both use HTTP. But SoapUI is not a web browser, and does not share the same restrictions.
A colleague solved this issue for me by pointing me to CORS Anywhere.
Install Node.js, if you don't already have it.
Install CORS Anywhere (npm install cors-anywhere).
Run CORS Anywhere (node cors-anywhere.js).
Instead of using your original non-CORS-enabled web service URL, use the domain and port in the resulting message from CORS Anywhere ("Running CORS Anywhere on ..."), with your original web service URL as the path component.
You can specify the CORS Anywhere port in an environment variable (or edit the default value in your local copy of cors-anywhere.js).
In my case, the production web service URL is hardcoded in the web page as a relative path. But I've added code that reads a URL from a parameter in the fragment identifier ("hash string"), overriding the default web service URL.
For example:
http://localhost:8081/mywebpage.html#url=http://localhost:8089/remote.domain.com:8085/servicename
where:
http://localhost:8081/mywebpage.html is my web page (I'm using http://, not file://, for this).
http://localhost:8089 is the CORS Anywhere proxy.
remote.domain.com:8085/servicename (you can drop the leading http://) is the remote, non-CORS-enabled web service.
I could point you 2 options to solve this:
Disable your browser CORS: Due the CORS enforcement is done by the browser you can just disable this during development in yourself browser. If you use Chrome, you must just set an parameter otherwise I may guess looking to the underneath image you are using Firefox, for this you have an extension to do this: https://addons.mozilla.org/pt-PT/firefox/addon/cors-everywhere/
Allow CORS in SOAP UI: It can take a little bit more effort than above solution but it fits very good when you need to share with mutiple teammates or just to make the solution attached to the mock service. To do this,you must add a response for the root of resource path that responds to OPTIONS and with headers you need for CORS with status 204 (or 200).
After, in the MockService you just need to add a script to grab these same headers in all calls that passes through.
Here is the article to solve this step-by-step:
https://medium.com/#andrelimamail/how-to-deal-with-cors-in-soap-ui-mock-services-or-anyother-f4cc55b3dccd
I'm looking at the source code for Microsoft.OWIN.Security.Google and am a bit confused and overwhelmed at how many classes there are to do such a simple thing (redirect, get a cookie, check it).
Can anyone explain how the various components fit together
Middleware
Extensions
etc
... so that I can write a custom provider
After some google-ing and trying different ideas in debugger I ended up with "copy-paste-edit" :)
here is a brief resume of classes
Extensions - nothing special, a helper:
// instead of using
app.Use(typeof(CustomAuthenticationMiddleware), app, options);
// you can use
app.UseCustomAuthentication(options);
Middlware - methods are used to attach authentication to owin pipeline
AuthenticationProvider - As I understand, this could be overriden outside, to be able to change some logic without rewriting whole thing. Has 2 methods:
Authenticated - is called when handler finishes all authentication in AuthenticationHandler.AuthenticateCoreAsync()
ReturnEndpoint which is called in AuthenticationHandler.InvokeAsync, just before external authetication.
But it appeared absolutely useless, when I tried to customize existing providers (google, facebook,...)
Handler - here is all the OAUTH2 functionality.
ApplyResponseChallengeAsync() - generates AuthorizationEndpoint URL and redirects useragent to authorization server
InvokeAsync() - handles the get to RedirectEndpoint (/signin-google or whatever was set up on authorization server) and returns the user to the starting controller(or callback). It is doing a redirect with all needed cookies set up
AuthenticateCoreAsync() - does all server side calls to authorization server. Creates all Identity.Claims necessary to create appropriate cookies before
I am trying to build a proxy that would serve requests to an internal site (hiding the origin) but at the same time inspect the packets and asynchronously post-process them.
E.g. let's say all SOAP calls to http://www.foo.com will go to http://192.168.1.1, and at the same time be stored in a DB for post analysis. The internal server is a black box, so changing something on it is out of this question scope.
Anyway, I have configured ARR, with reverse proxy, made URL rewrite filter with wildcards, all works flawless. Then, I tried to add an managed HttpModule written in C#, and hooked to Application_BeginRequest and Application_EndRequest. I am able to access request headers, response headers on end request (app pool being in integrated mode) and even able to read response content from the outputstream by setting a filter on Response.Filter, that caches all writes in an additional memory stream.
The problem is that the moment I try to read (inside the module BeginRequest handler) the input stream from the request, ARR stays a while and throws a
HTTP Error 502.3 - Bad Gateway The
operation timed out Handler
ApplicationRequestRoutingHandler
Error Code 0x80072ee2
So it times out.
Looking with Failed Request Tracing I see:
MODULE_SET_RESPONSE_ERROR_STATUS
Warning
ModuleName="ApplicationRequestRouting",
Notification="EXECUTE_REQUEST_HANDLER",
HttpStatus="502", HttpReason="Bad
Gateway", HttpSubStatus="3",
ErrorCode="2147954402",
ConfigExceptionInfo=""
SET_RESPONSE_ERROR_DESCRIPTION Warning
ErrorDescription="The operation timed
out"
Now any similar posts on the net didn't helped as this isn't a timeout error (proxy has 120 seconds setting, page answers in under 100 ms), and the moment I comment the code of the handler that tries to read FORM data or InputStream data, everything works as a charm.
Even if I set the position of the inputstream to 0 after reading it, I still get timeouts.
If I read the input stream on EndRequest, it gets 0 bytes, even if it was a POST request. (which is clearly wrong)
Does ARR has a bug in the fact that I try to read an input stream before it tries to re-route it?
Things used: Windows Server 2008 R2
IIS 7.5 ARR v2 .Net Framework 3.5
module
Ideas?
Thanks
/Cosmin
If you can switch to .Net Framework 4, there is a solution for this.
After you are done with your BeginRequest/EndRequest in your HttpModule event handler, add a call to HttpRequest.InsertEntityBody.
/* BeginRequest event: Executes before request is processed */
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpRequest request = application.Context.Request;
// Do something with request
DoMyOwnRequestProcessing(request);
// After you finish, make sure IIS gets the entity body
// For example, Application Request Routing needs this
request.InsertEntityBody();
}
Take a look at this on MSDN: HttpRequest.InsertEntityBody.
I know this is a year old question, but I just went through the same thing and found a solution. So, I'm posting it here for anyone else that runs into this.
In my case I only saw the timeout issue with POST requests.
It appears that the 2.0/2.1 ARR assumes that the input stream will be at the start of the posted data. However, the following code (for example) will break this assumption:
HttpContext context = HttpContext.Current;
HttpRequest request = context.Request;
string value = request.Params["Name"];
The key is how Params is described
Gets a combined collection of System.Web.HttpRequest.QueryString,
System.Web.HttpRequest.Form, System.Web.HttpRequest.ServerVariables,
and System.Web.HttpRequest.Cookies items."`
When the request is a POST, accessing Params will read the posted data from the input stream, invalidating ARR's assumption. As will reading from the input stream.
I knew the data I needed was in the query string, not the posted data, so I worked around this by accessing the QueryString instead of Params. This avoids reading the posted data and works fine for me.
string value = request.QueryString["Name"];
This issue appears to be fixed in ARR 2.5.
Upgrading ARR appears to be the only solution if you need to access posted data before handing off to ARR. The key is to let HttpRequest handle acquiring the data into Params. If you read it directly it will not work.
I just ran into this bug and your experiences helped me determine the root cause.
My main server is MVC based and it looks at the Request.Form values in the Application_BeginRequest method. If the form values are accessed ARR fails to forward the body of a HTTP POST request. GET requests will work fine since there is no body.
I have routes.IgnoreRoute ("Forum/{*pathInfo}"); as a registered route but ARR runs as a HttpModule and doesn't kick-in until later in the pipeline. That means my MVC based application is given the opportunity to access the content of the POST body which somehow prevents ARR from accessing the body itself and forwarding it to the proxy'd server.
Here is Cosmin's related post on the iis.net forums: ARR 2.0 BUG - combined with managed http module timeout on read inputstream
In my application I have all myserver.com/Forum/* requests being reverse proxy'd to a separate application on another server. So I simply checked the HttpContext.Current.Request.Url in my MVC application's Application_BeginRequest method to make sure it does not contain /Forum before accessing the Request.Form values. Once I did that the POST bodies made it through ARR just fine.
UPDATE: after further testing it appears that there are still problems with ARR as POST from non-authenticated users still fails. Instead of the main website being MVC I created a dummy IIS .NET 4.0 website with a single Default.html document. But I still ran into problems with POST requests and ARR. Then I switch the application pool to ASP.NET 2.0 and what do you know, it works. At this point I have to assume that something in the .NET 4.0 pipeline is accessing the input stream which prevents ARR from accessing the input stream itself in order to forward the POST body.
按照正常来说,再iis网站界面会有一个application request
routing cache 的 icon, 可以点击 设置timeout 但是这里没有显示
找到了 官方说明可以用命令行解决这个问题
https://blogs.iis.net/richma/502-3-bad-gateway-the-operation-timed-out-with-iis-application-request-routing-arr
blogs.iis.net
执行以下命令,然后重启下网站服务
进入到C:\Windows\System32\inetsrv 打开管理员命令行工具执行以下命令
appcmd.exe set config -section:system.webServer/proxy /timeout:"00:00:45" /commit:apphost
重启下网站服务
我写的原文地址
https://zhuanlan.zhihu.com/p/157557980
I've got an ASP .Net application running on IIS7. I'm using the current url that the site is running under to set some static properties on a class in my application. To do this, I'm getting the domain name using this (insde the class's static constructor):
var host = HttpContext.Current.Request.Url.Host;
And it works fine on my dev machine (windows XP / Cassini). However, when I deploy to IIS7, I get an exception: "Request is not available in this context".
I'm guessing this is because I'm using this code in the static constructor of an object, which is getting executed in IIS before any requests come in; and Cassini doesn't trigger the static constructor until a request happens. Now, I didn't originally like the idea of pulling the domain name from the Request for this very reason, but it was the only place I found it =)
So, does anyone know of another place that I can get the host domain name? I'm assuming that ASP .Net has got to be aware of it at some level independent of HttpRequests, I just don't know how to access it.
The reason that the domain is in the request is...that's what's being asked for. For example these are a few stackexchange sites from http://www.stackexchangesites.com/:
http://community.ecoanswers.com
http://www.appqanda.com
http://www.irosetta.com/
If you ping them, you'll see they all point to the same IP/Web Server and be served by the same app (or multiple apps in this case, but the example holds if it was one big one)...but the application doesn't know which one until a host header comes in with the request asking the server for that site. Each request may be to a different domain...so the application doesn't know it.
If however it doesn't change, you could store it as an appSetting in the web.config.
Use global.asax or write a HttpModule and subscribe to start request events. You will have the request passed into your event handler.
Use this instead:
HttpRuntime.AppDomainAppVirtualPath
Or if you want the physical path:
HttpRuntime.AppDomainAppPath
For further reading:
http://weblogs.asp.net/reganschroder/archive/2008/07/25/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-application-start.aspx