Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample services don't support CORS, which makes JS clients hard to consume #29

Closed
dingeins opened this issue Aug 4, 2015 · 37 comments
Closed
Labels

Comments

@dingeins
Copy link

dingeins commented Aug 4, 2015

The original issue is here: OData/odataorg.github.io#66

Quoting the author:

And thus can not be used from a web browser for testing JS, for instance.
Example request:

OPTIONS http://services.odata.org/V3/Northwind/Northwind.svc/$metadata HTTP/1.1
Accept: */*
Origin: http://localhost:50854
Access-Control-Request-Method: GET
Access-Control-Request-Headers: accept, maxdataserviceversion
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: services.odata.org
Content-Length: 0
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
MaxDataServiceVersion: 3.0

and it's response:

HTTP/1.1 501 Not Implemented
Cache-Control: private
Content-Length: 195
Content-Type: application/xml;charset=utf-8
Expires: Thu, 30 Jul 2015 18:31:03 GMT
Vary: *
Server: Microsoft-IIS/8.0
X-Content-Type-Options: nosniff
DataServiceVersion: 1.0;
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Accept, Origin, Content-Type, MaxDataServiceVersion
Access-Control-Expose-Headers: DataServiceVersion

Notice the response headers claim to allow CORS, but the response is Not Implemented.

Workaround

None that I know. I'd tipically just uso JSONP, but $metadata can not be requested by JSONP.

The reporter was able to reproduce this problem against V2, V3 and V4 Northwind services. He hasn't tested TripPin services yet, but they may have the same problem.

@dhiraj
Copy link

dhiraj commented Sep 7, 2015

I wanted to try and make an OData v4 CRUD client to Trippin using AngularJS but it is simply blocked without CORS support.

Any suggestions on how to enable it?

@dhiraj
Copy link

dhiraj commented Sep 7, 2015

For those who come looking, I have a workaround that seems to be working with the Trippin sample.

I used the enable-cors site http://enable-cors.org/server_iis7.html

@dhiraj
Copy link

dhiraj commented Sep 7, 2015

I spoke too soon, the server does not reply to the OPTIONS preflight approval request fired from the browser and the workaround does not actually work. :(

@KnoxArg
Copy link

KnoxArg commented Oct 28, 2015

Hi! I want to know if this will get resolved in the future.

Cheers

@akorchev
Copy link

Any plans to fix that? Access-Control-Request-Method should allow PATCH, PUT, POST and DELETE.

@gowrisankar22
Copy link

Hi All,
I am also facing the same issue. please help me for fixing this issue

@boghyon
Copy link

boghyon commented Jul 30, 2018

Would be nice if sample services can finally support CORS. I'd like to avoid using proxy servers, especially when it comes to quick demos.

@BartNetJS
Copy link

same for me

@gregorwolf
Copy link

Does someone take care about this issue? Also SAP uses this sample service in their OData V4 example and I get the "Status Code: 404 Not Found" back when removing the mockserver. How can I help you @biaol-odata @markdstafford @mikepizzo @xuzhg to fix this?

@boghyon

This comment has been minimized.

@boghyon
Copy link

boghyon commented Feb 26, 2019

@mikepizzo Awesome, but this issue shouldn't be closed yet as the original issue was referring to V2 and V3 Northwind services. Or does #113 cover them too?

@mikepizzo mikepizzo reopened this Feb 26, 2019
@mikepizzo
Copy link
Member

Re-opening as V2/V3 not yet fixed, and we have not yet deployed the fix to the hosted services.

@mminnick
Copy link

Is there any word on this being deployed to the service yet?

@boghyon
Copy link

boghyon commented Feb 5, 2021

So the day has come. The popular public proxy server cors-anywhere.herokuapp.com is officially unusable making the consumption of sample OData services much harder. 🙁 Sure, we can host our own proxy server, but a better solution is to actually support CORS as suggested below:

What should current users of CORS Anywhere do in response to this announcement?

If possible, try to avoid the need for a proxy at all. CORS Anywhere works by combining proxy functionality with CORS. You may not need proxy functionality, if the web service that you are trying to access already supports CORS. This is the preferred solution because it is faster and more reliable.

@mikepizzo How can we help to fix this issue? Would it be possible to deploy at least the V4 fix to the hosted sample services?

@mikepizzo
Copy link
Member

mikepizzo commented Feb 5, 2021

Sorry; I didn't realize this was still an issue for folks. The service has supported returning CORs headers on GET/POST/PUT/PATCH requests, but didn't previously support OPTIONs for CORs preflight (OPTIONs requests returned 404 not found).

TrippinRESTier sample service now supports OPTIONs for CORs preflight (basically, and request to OPTIONS will return the CORs headers and 200 OK.

@boghyon -- let me know if you're still running into issues with Trippin.

@boghyon
Copy link

boghyon commented Feb 5, 2021

@mikepizzo Thank you for the reply. Unfortunately, it still fails:

  1. Open https://embed.plnkr.co/IAM5TBfKWaTW8vbg?show=manifest.json,preview&deferRun
  2. Open the browser console and start the app in the preview pane.
  3. Browser reports:

    Access to XMLHttpRequest at https://services.odata.org/TripPinRESTierService/(S(qatUyq))/$metadata from origin https://run.plnkr.co has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

@mikepizzo
Copy link
Member

mikepizzo commented Feb 5, 2021

From the error message it looks like we are setting the Access-Control-Allow-Origin header twice. Indeed, as I look at the code, it looks like we tried to support it through a CORS add-in as well as specifically set the header (just to be sure) which may be leading to having it set in two places.

I haven't been able to validate this in Fiddler (which I think messes with my Origin request header), and I don't see the error in embed.plnkr.co (I just get "no data") but I've uploaded a possible fix to a staging service.

Can you try https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/$metadata and let me know if it fixes the issue? If so, I'll deploy the same fix to production (which is simply removing the explicit setting of the Access-Control-Allow-Origin header and relying on the CORS library.)

@boghyon
Copy link

boghyon commented Feb 5, 2021

I updated the service URL but unfortunately the error remains.

  1. Basically, only the "uri" part needs to be changed in the manifest.json file (without $metadata but with / at the end):
    {
      "dataSources": {
        "myDataSource": {
          "uri": "https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/",
           "...": "..."
        }
      }
    }
  2. Same error in the console:

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

@mikepizzo
Copy link
Member

@boghyon -- Sorry, I missed copying over a config file with the change for the redundant Allow-Origin header. Please try https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/ one more time, and let me know what you find.

Unfortunately I don't see an error when I go to https://embed.plnkr.co/IAM5TBfKWaTW8vbg?show=manifest.json,preview&deferRun, I just get "No Data".

@boghyon
Copy link

boghyon commented Feb 7, 2021

@mikepizzo I do get $metadata now successfully which is great. However, the service doesn't seem to set OData-Version in response headers, which causes an error in the client, hence "No Data" in the UI.

Error log from the browser console:

Error: Expected 'OData-Version' header with value '4.0' but received value 'null' in response for https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/People?$select=FirstName,UserName&$skip=0&$top=100

According to the OData V4.0 spec 8.1.5:

8.1.5 Header OData-Version

OData services MUST include the OData-Version header on a response to specify the version of the protocol used to generate the response.

Same spec can be found in the V4.0.1 section 8.1.5.

Maybe I'm misinterpreting the specification or overlooking something. Nevertheless, the client (UI5) does seem to expect OData-Version: '4.0' in the response header.


And according to https://github.com/SAP/openui5/blob/3375063aa9fc6344440978f2f7cb5b93a249da31/src/sap.ui.core/src/sap/ui/model/odata/v4/lib/_Requestor.js#L666-L668, the OData-Version header is optional only for $batch request responses.


Edit: I just noticed that there is an OData-Version header set in the GET response but not in OPTIONS:

Response-header-Odata-version-missing

Here the headers from GET:
Response-header-Odata-version-not-missing-in-GET

@boghyon
Copy link

boghyon commented Feb 8, 2021

@mikepizzo Now the question is, does the service have to add the OData-Version header to the OPTIONS response too? Couldn't find anything in the specification. If not, I guess this is a client issue.

@mikepizzo
Copy link
Member

I generally try to make clients a little lax so that they can work with a broader range of services, which means I would probably try and default the client to OData-Version: 4.0 if there was no response header, but that's just me. I know there are other equally valid design philosophies out there as well.

We don't really talk about OPTIONS in the OData spec, but I would generally expect the same header rules apply as for the actual request, so I've added the OData-Version header to Trippin_Staging and confirmed that both the Access-Control-Allow-Origin and OData-Version headers are returned.

I still get No data in the browser for the Open Ui5 app, but perhaps you'll have more luck?

@boghyon
Copy link

boghyon commented Feb 8, 2021

I generally try to make clients a little lax so that they can work with a broader range of services,

I agree. UI5 (SAP's client-side JS framework) is quite strict when it comes to OData V4 currently. I asked if they could at least ignore the missing headers in preflight responses and make assumptions if possible.

I've added the OData-Version header [...] I still get No data in the browser for the Open Ui5 app

Unfortunately, the OData-Version header is still missing in the OPTIONS response.

image

@mikepizzo
Copy link
Member

I am seeing OData-Version being returned in the OPTIONS request against the latest Trippin_Staging endpoint:

image

Curious what is different between our requests.

Can you show the full request (including all request headers)?

@boghyon
Copy link

boghyon commented Feb 8, 2021

Request

Request URL: https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/People?$select=FirstName,UserName&$skip=0&$top=100
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: 137.117.17.70:443
Referrer Policy: strict-origin-when-cross-origin

Request headers

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,de-DE;q=0.8,de;q=0.7,ko;q=0.6,ko-KR;q=0.5
Access-Control-Request-Headers: content-type,odata-maxversion,odata-version,x-csrf-token
Access-Control-Request-Method: GET
Cache-Control: no-cache
Connection: keep-alive
Host: services.odata.org
Origin: https://run.plnkr.co
Pragma: no-cache
Referer: https://run.plnkr.co/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: <removed>

Response headers

Access-Control-Allow-Headers: content-type,odata-maxversion,odata-version,x-csrf-token
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Content-Length: 0
Date: Mon, 08 Feb 2021 22:00:27 GMT
Expires: -1
Pragma: no-cache
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

(Tried to upload the HAR file for better analysis but GitHub doesn't allow it)


Reproducible with https://embed.plnkr.co/IAM5TBfKWaTW8vbg?show=manifest.json,preview&deferRun (Headers copied from Chromium's Network tab devtool).

@boghyon
Copy link

boghyon commented Feb 8, 2021

Just tried Postman with the above request headers. Interestingly, only when there is no Origin header, I get the OData-Version header.

✔ Without the Origin header:
OData-V4-TripPin-CORS-without-Option-header
But then, Access-Control-Allow-Origin and Access-Control-Allow-Headers are missing in the response (Shouldn't they always be included in preflight responses? 🤔).


❌ With the Origin header:
OData-V4-TripPin-CORS-with-Option-header
Here, the headers Access-Control-Allow-Origin and Access-Control-Allow-Headers are included, but no OData-Version.

@boghyon
Copy link

boghyon commented Feb 10, 2021

AFAIK, the Origin header is always added by the browser when a request is sent, so omitting it is not an option.

But more importantly, after discussing with the V4 maintainers from UI5 (SAP/openui5#3150), the reason why the browser is unable to access the OData-Version header might be that the server is not including the Allow-Control-Expose-Headers Access-Control-Expose-Headers in the response.

From MDN:

The Access-Control-Expose-Headers header lets a server whitelist headers that Javascript (such as getResponseHeader()) in browsers are allowed to access.

And JS clients (e.g. UI5) depend on the API getResponseHeader() internally in order to access custom response headers (e.g. the OData-Version).

To sum it up:

  • Access-Control-Allow-Headers indicates which request headers the client is allowed to send,
  • whereas Access-Control-Expose-Headers indicates which response headers the client is allowed to read.

Could you add Allow-Control-Expose-Headers: * Access-Control-Expose-Headers: * too when sending responses? Probably a wildcard (*) is sufficient as its value since it's a demo service.

@boghyon
Copy link

boghyon commented Feb 23, 2021

@mikepizzo Is there a chance to continue with this issue? Adding Allow-Control-Expose-Headers: * Access-Control-Expose-Headers: * should allow the client accessing the OData-Version header value.

Would be nice if this issue could be reopened as it's not fully resolved.

@mikepizzo
Copy link
Member

Yes -- hope to get to this after the long week-end.

@mikepizzo
Copy link
Member

@boghyon -- sorry for the delay; the changes were trivial, but some system changes to the production environment made it difficult for me to push the changes.

Please verify that https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/ works correctly (i.e., that OPTIONS now returns the Allow-Control-Expose-Headers:* as well as the OData-Version header).

Assuming this addresses the issue, I will push the changes from staging to production.

Thanks for your patience ---

-Mike

@gregorwolf
Copy link

Hi @mikepizzo ,

I've created a REST Client script at odata.org-Trippin.http and included the results. Please check if that is what you expect.

Best regards
Gregor

@boghyon
Copy link

boghyon commented Jun 15, 2021

(i.e., that OPTIONS now returns the Allow-Control-Expose-Headers:* as well as the OData-Version header).

@mikepizzo Sorry, I just realized I that I wrote Allow-Control-Expose-Headers several times in my previous comments. My bad!
It should be of course Access-Control-Expose-Headers. Could you please updated it again?

@gregorwolf Thanks for the script! Didn't know about the extension.


Request from the client with the following headers

### Test Trippin_Staging
OPTIONS https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/People?$select=FirstName,UserName&$skip=0&$top=100
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type,odata-maxversion,odata-version,x-csrf-token
Access-Control-Request-Method: GET
Cache-Control: no-cache
Connection: keep-alive
Host: services.odata.org
Origin: https://run.plnkr.co
Pragma: no-cache
Referer: https://run.plnkr.co/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36

Response headers

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/10.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: content-type,odata-maxversion,odata-version,x-csrf-token
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 15 Jun 2021 09:20:08 GMT
Content-Length: 0

Interestingly, neither OData-Version: 4.0 nor the AllowAccess-Control-Expose-Headers: * is included if the request is sent with the header Access-Control-Request-Method: GET which the app requests.
Without Access-Control-Request-Method: GET, the response headers mentioned at Gregor's odata.org-Trippin.http are returned.

@mikepizzo
Copy link
Member

Okay; this has been a strange trip....

If I enable CORS using the EnableCorsAttribute, then the appropriate CORs headers are automatically added to supported methods. Leveraging that just required that I add (and register) an OPTIONS controller method to handle he preflight requests.

However, if CORS is enabled using the EnableCorsAttribute, and Access-Control-Request-Method: GET request header is specified, then:

  1. The Access-Control-Expose-Headers response header is not added, and
  2. My OPTIONS controller never gets called and I don't have an opportunity to add the OData-Version response header.

So, I had to add a message handler that, for OPTIONS requests, adds the OData-Version and (if it's not already present) the Access-Control-Expose-Headers response header.

I have uploaded the latest to staging -- you can try it at https://services.odata.org/Trippin_Staging/(S(iw1anra4xygjyssbeef0yeyy))/.

Please let me know if this still has any issues; I think I now have hooks in all the right places in code to modify the behavior as required.

@boghyon
Copy link

boghyon commented Jun 15, 2021

@mikepizzo

Wow.. Frodo done

I just tested basic CRUD, with and without $batch. So far everything seems to work well!
Thank you so much for all the work and support. You're the best!

@mikepizzo
Copy link
Member

Thanks for the confirmation -- glad we finally got it sorted!

I've pushed to production, so I'll close this issue; please let me know if you encounter any further issues.

@boghyon
Copy link

boghyon commented Jun 30, 2021

@mikepizzo Would it be possible to enable CORS for other services as well? Especially the V2 Northwind is still widely used for demos and tutorials.

@mikepizzo
Copy link
Member

@boghyon -- please go ahead and open a separate issue to track supporting CORS in the other services, and reference this one for the history/background.

The V2 stuff is pretty old, and in a different repo that's not actively maintained, so I'm not sure when we'll have cycles to go back and update, but at least we'll be able to track the request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants