This question already has an answer here:
How to send an HTTP 4xx-5xx response with CORS headers in an ASPNET.Core web app?
(1 answer)
Closed 3 years ago.
I am migrating and existing angular js app to angular while sticking to an existing api.
I tried to implement a logon (ASP NET / OWIN) and encountered following situation after making a http request to logon endpoint:
All possible server responses (2xx,4xx,5xx) could not be fetched in javascript code.
After researching I found the cause: CORS
This is because new angular app runs on different port than the locally hosted api while the old angular js ab is being hosted on the same port.
So I added following to ValidateClientAuthentication in API:
context.Response.Headers.Append("Access-Control-Allow-Origin", "http://localhost:4200");
context.Response.Headers.Append("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Now I am finally able to fetch 2xx responsed but still do not get 4xx and 5xx results.
Why is that?
And how is it possible that API calls to other endpoints of same API do not require setting those headers?
UPDATE:
I receive this error message for all 4xx responses even if I have added those headers in API:
:63323/api/logon:1 Failed to load resource: the server responded with a status of 400 (Bad Request)
vendor.js:68504 ERROR TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at subscribeTo (vendor.js:158625)
at subscribeToResult (vendor.js:158782)
at CatchSubscriber.error (vendor.js:151269)
at MapSubscriber.Subscriber._error (vendor.js:147999)
at MapSubscriber.Subscriber.error (vendor.js:147979)
at XMLHttpRequest.onLoad (vendor.js:24621)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.js:2785)
at Object.onInvokeTask (vendor.js:70070)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.js:2784)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (polyfills.js:2557)
UPDATE 2:
this is the code that handles http request:
return this.httpClient
.post(this.appConfigService.buildAuthorizationUrl('/logon'), data, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
.toPromise()
.then(
(response: any) => {
// hit when 2xx and cors headers present
},
(error: any) => {
// never hit
}
)
.catch(error => {
// never hit
});
UPDATE 3:
As I understood CORS middleware might(?) drop all heades in case 4xx or 5xx responses which will prevent browsers from fetching responses. But like you can see my 400 response from a different endpoint on my api seems to be valid:
HTTP/1.1 400 Bad Request
Cache-Control: private,no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Length: 112
Content-Type: application/json; charset=utf-8
Expires: 0
Server: Microsoft-IIS/10.0
Access-Control-Allow-Origin: http://localhost:4200
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcS2F5IFphbmRlclxEb2N1bWVudHNcZXZlXFNvdXJjZVxJbmZvcnNIVC5HZW5lc2lzQXBpXGF1ZGl0dHJhaWw=?=
X-Powered-By: ASP.NET
X-UA-Compatible: IE=edge
Date: Wed, 06 Nov 2019 16:01:08 GMT
[{"ErrorMessage":"Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.","PropertyName":"Exception"}]
still I encounter the mentioned error message.
Most likely your ASP.NET backend doesn't have CORS enabled, according to the fact that if you set it manually you're able to make a request. If you set a header (e.g. Content-Type) to your request, whether it was manually set or automatically, Angular sends a preflight request for the backend to determine if the resource can be accessed with the specific request headers or not.
There's a built-in support for preflight request handling, which you can enable using attributes to your controllers, and a little configuration in the initial startup class.
For ASP.NET (.NET Framework based) check this guide: https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
For ASP.NET Core check this one: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.0
Related
I have an application that has been successfully using HTTP batch requests to insert, edit, and delete events via the Google Calendar API. In the last couple of days, the individual requests within the batches have started returning 404 errors (although the batch itself gets a 200 success response). Making those same requests as individual requests using the same authorization header is still working.
I'm pretty sure that this isn't related to the forthcoming shutdown of Google's global HTTP batch endpoints because we're using https://www.googleapis.com/batch/calendar/v3 as our endpoint.
Here's an example of what I'm trying to do:
https://www.googleapis.com/batch/calendar/v3
Authorization: Bearer your_auth_token
Content-Type: multipart/mixed; boundary=batch_google_calendar
--batch_google_calendar
Content-Type: application/http
Content-ID: <item-0-batchevent#example.com>
POST calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
Content-Type: application/json
{"summary":"batch API test","start":{"date":"2020-07-31"},"end":{"date":"2020-07-31"}}
--batch_google_calendar--
And the response is:
--batch_3J6sfuPtVQbjZLcpUe06245gKlO31YnC
Content-Type: application/http
Content-ID: <response-item-0-batchevent#example.com>
HTTP/1.1 404 Not Found
Vary: Origin
Vary: X-Origin
Vary: Referer
Content-Type: application/json; charset=UTF-8
[{
"error": {
"code": 404,
"message": "URL path: /v3/calendars/your_calendar_id#group.calendar.google.com/events could not be resolved. Maybe there is an error parsing the batch item.",
"status": "NOT_FOUND"
}
}
]
--batch_3J6sfuPtVQbjZLcpUe06245gKlO31YnC--
And here's an example of an individual request that's working:
https://www.googleapis.com/calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
Authorization: Bearer your_auth_token
Content-Type: application/json
{"summary":"API test","start":{"date":"2020-07-31"},"end":{"date":"2020-07-31"}}
Why might the individual request be succeeding but the batch request fail?
Google gave a helpful reply via their issue tracker: there was an error in the way that batch entry paths were specific in my application. This had worked without errors until last week, so I think something must have changed at their end to make it less tolerant of mistakes.
The error we had made was omitting the leading slash in the path in each batch entry. Here's what we were doing:
POST calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
And here's what we should have been doing:
POST /calendar/v3/calendars/your_calendar_id#group.calendar.google.com/events
I hope that this might be helpful to anyone else who ever finds themselves in a similar situation!
I used visual studio net 2.1 and a local sql database for my site on AWS. The first page comes up but when I try to put in a comment and the code routes to another page on my site I get this console error and the page is not routed to in the browser:
Access to XMLHttpRequest at 'https...' from origin 'https://www.....dev' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is the startup.cs file
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
});
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
For some CORS requests, the browser sends an additional OPTIONS request before making the actual request. This request is called a preflight request. The browser can skip the preflight request if all the following conditions are true:
The request method is GET, HEAD, or POST.
The app doesn't set request headers other than Accept, Accept-Language, Content-
Language, Content-Type, or Last-Event-ID.
The Content-Type header, if set, has one of the following values:
application/x-www-form-urlencoded
multipart/form-data
text/plain
AllowAnyOrigin affects preflight requests and the Access-Control-Allow-Origin header.
The preflight request uses the HTTP OPTIONS method. It may include the following headers:
Access-Control-Request-Method: The HTTP method that will be used for the actual request.
Access-Control-Request-Headers: A list of request headers that the app sets on the actual request.
Access-Control-Allow-Methods
Try allowing specific origins, methods and headers.
For more information can check the below link:
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1#preflight-requests
THe answer is to enable the api-gateway in AWS
In API Gateway Console click the Actions dropdown and select Deploy API.
This eliminated the CORS issue.
I am using MSXML6 in a vbscript-like code to download data over HTTP. But the server now requires connections to upgrade to HTTPS.
This is causing the xmlhttp object to fail with the error "msxml6.dll: Access is denied."
Set http = CreateObject("msxml2.xmlhttp.6.0")
http.open "Get", URL, False 'false is for 'async'
http.send
Using a sniffing tool, the operation stops after receiving the redirection-to-https response, and the error is generated without further details.
Requesting http://host/doc.php (plain http), the returned headers look something like this:
HTTP/1.1 301 Moved Permanently
Date: Fri, 19 Jul 2019 23:59:30 GMT
Content-Type: text/html; charset=iso-8859-1
Transfer-Encoding: chunked
Connection: keep-alive
Location: https://host/doc.php
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
However, if the requested URL is already https, the operation resumes normally without any complaint.
Is there anything I can do on the server side to convince xmlhttp to upgrade the connection to https peacefully?
Updating the code in the client application is out of the question as it is a legacy application with so many users out there using it, without an update mechanism.
Asking the users to update the URL adding an "s" after http is workable but too much hassle, as reaching them to tell them is also not easy at all.
Edit:
The conclusion is in this comment. The summary is that this is a client-side protection feature and it cannot be overridden from the server side.
The problem as mentioned in Xmlhttp request is raising an Access Denied error is you need to use the Server version of XMLHTTP that isn't restricted to accessing sites trusted by IE and restricted by the IE security policies. This is because XMLHTTP is designed for client-side whereas ServerXMLHTTP is specifically designed for server-side usage.
I created a new solution from WebApi .Net Core 2.0 template in Visual Studio.
I added the following in startup.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddHttpCacheHeaders(opt => opt.MaxAge = 600);
services.AddResponseCaching();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseResponseCaching();
app.UseHttpCacheHeaders();
app.UseMvc();
}
Then, with postman, I hit http://localhost:xxxx/api/values which is a endpoint created by the template and that returns ["value1","value2"]
Note that I made sure that Postman doesn't send no-cache header (in Postman settings).
The HttCacheHeaders service comes from that repo. It adds HTTP Cache Headers. So my endpoint response header is:
Cache-Control: public,max-age=600
Content-Type: application/json; charset=utf-8
Date: Fri, 29 Sep 2017 14:02:29 GMT
ETag: C5DFA8974BB722D27E71EE50D3D14625
Expires: Fri, 29 Sep 2017 14:03:29 GMT
Last-Modified: Fri, 29 Sep 2017 14:02:29 GMT
Server: Kestrel
Transfer-Encoding: chunked
Vary: Accept, Accept-Language, Accept-Encoding
X-Powered-By: ASP.NET
X-SourceFiles: =?UTF-8?B?................
The problem is that nothing gets cached. the Ouput windows only shows The response could not be cached for this request.
So I'm a bit lost on how to use ASP.NET Core ResponseCaching Middleware.
Update
If I don't use the HttCacheHeaders service but add [ResponseCache(Duration = 600)] to the action of my controller, the cache works.
Note that reason that I want to use HttCacheHeaders is for ETag and Last-Modified to later do Validation Caching as well as Expiration Caching.
This is kind of a non-answer, with some troubleshooting tips.
I tried your exact code & it worked fine for me. Maybe a bug has been fixed in the HttpCacheHeaders or ResponseCaching repo?
Unfortunately, debugging the server-side ResponseCaching is tricky because it has weird rules & there's not adequate logging. When I've had similar issues with it in the past I've had to pull down Microsoft's source code to step through it & find the issue with my code.
The note you found in the output window "The response could not be cached for this request" is a clue.
There's 2 parts to the server-side caching of a request. The server has to prime the cache the first time the url is requested. It will serve the cached version the 2nd time it's requested. Pay attention to when the error message shows up, if it's on the 1st or 2nd request. That'll tell you if it couldn't be stored in the cache or if it couldn't be retrieved from the cache.
The rules for both storage & retrieval are in this source code file: https://github.com/aspnet/ResponseCaching/blob/3bf5f6a1ce69b65c998d6f5c739822a9bed4a67e/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs
Your "Cache-Control:public,max-age=600" header should match these rules just fine.
You already found the no-cache/no-store "gotcha". There are also a few other others with ResponseCaching to watch out for:
Authenticated requests & responses with set-cookie won't be cached. Only requests using GET or HEAD method will be cached. If the QueryString is different, it'll make a new cache entry. Also, usually you'll want a "Vary" header to prevent caching if certain conditions of a request differ from the previously-cached request (example: user-agent, accept-encoding, etc).
On a side note, honoring the no-cache/no-store request headers was probably a poor design choice since ASP.Net Core's ResponseCache will most likely be used by a server who owns the response, rather than an intermediary cache like a CDN/ISP. I've extended the base ResponseCache with an option to disable honoring these headers (as well as serialize the cache to disk, rather than in-memory only). It's an easy drop-in replacement for the default cache.
You can find my extension here: https://github.com/speige/AspNetCore.ResponseCaching.Extensions https://www.nuget.org/packages/AspNetCore.ResponseCaching.Extensions
HttpCacheHeader is not a cache store.
This middleware handles the "backend"-part: it generates the correct cache-related headers, and ensures a cache can check for expiration (304 Not Modified) & preconditions (412 Precondition Failed) (often used for concurrency checks).
Source
I'm using Asp.Net Core RC2 and Kestrel as my web server. I need to ensure that requests (in this case all of them) are responded to with a no-cache header so that the browsers get the newest version (not 304).
Is there a way in Startup to configure Kestrel or a way to inject this step into the pipeline?
EDIT: no-store may be a better choice in my situation: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching "no-store Response is not allowed to be cached and must be fetched in full on every request."
You can use middleware to work with headers. For example, you can force no-cache cache-control by adding the following to the top of your Startup's Configure method:
app.Use(async (httpContext, next) =>
{
httpContext.Response.Headers[HeaderNames.CacheControl] = "no-cache";
await next();
});