apache / cordova-ios

Apache Cordova iOS
https://cordova.apache.org/
Apache License 2.0
2.15k stars 987 forks source link

cordova ios ajax call not working after upgraded to ios 6.2 (uiwebview to wkwebview) #1241

Closed chrnM closed 1 year ago

chrnM commented 1 year ago

Issue Type

Description

Information

Due to xcode13 update we updated xcode to latest app is working in previous xcode 12 and after update not able to get the ajax response from server getting {"0":{"readystate":0,"status":0,"statustext":"error"},"1":"error","2":""} response

Command or Code

Environment, Platform, Device

cordova ios 6.2 xcode13

Version information

What are relevant versions you are using? For example: cordova ios platform:6.2 cordova plugins in my project @globules-io/cordova-plugin-ios-xhr 1.2.4 "Cordova WKWebView File XHR Plugin" cordova-plugin-androidx 3.0.0 "cordova-plugin-androidx" cordova-plugin-app-version 0.1.12 "AppVersion" cordova-plugin-dialogs 2.0.2 "Notification" cordova-plugin-file 6.0.2 "File" cordova-plugin-inappbrowser 5.0.0 "InAppBrowser" cordova-plugin-sim 1.3.3 "SIM" cordova-plugin-spinner 1.1.0 "Spinner Plugin" cordova-plugin-whitelist 1.3.4 "Whitelist" cordova-support-android-plugin 1.0.2 "cordova-support-android-plugin" cordova-support-google-services 1.4.1 "cordova-support-google-services"

Checklist

breautek commented 1 year ago

Layout constraints is unrelated to AJAX/XHR requests in webviews.

getting this repsonse {"0":{"readystate":0,"status":0,"statustext":"error"},"1":"error","2":""} while ajax call

This is consistent with CORS failure. Usually however, you'll see a more explicit CORS error in the javascript console. I've talked about CORS on my blog quite extensively but the gist of it is:

CORS is a security mechanism for browser users. Don't try to make sense of it, it's not a security mechanism for your app, and it doesn't really make much sense in cordova environments, however WKWebView enforces it and there is no API to disable it, so our only choice is to work around it.

If you're making a XHR request to a remote server, the remote server must be configured to respond with the appropriate CORS headers, as well as the OPTIONS method version of any request.

If you're making a XHR request to a local resource, there's a few different recommendations based on your circumstances: If possible, don't make XHR requests to local resources, and instead use the file plugin to read local files.

If a framework that you're using is doing requests to local resources (common in angular frameworks), then I'd recommend using schemes. Using schemes will change the origin, and thus will change web storage containers. As a result, content in web storage including local storage or cookies will be inaccessible. however, if you're upgrading from UIWebView to WKWebView, you will face the same challenge anyway.

To use schemes, simply add the following preferences to your config.xml file:

<preference name="scheme" value="app" />
<preference name="hostname" value="localhost" />

Using schemes has several advantages and it might be worth doing this regardless as part of your UIWebView to WKWebView migration.

If schemes is not an option for whatever reason, and you still need to use AJAX against local resources, then your last option is to rely on third-party plugins such as Oracle's XHR fix plugin. These plugins generally works by rewriting the browser XHR implementation and redirects the request to the native side, where native requests aren't bound by CORS. This is the least ideal path, but is a path that was successful especially for those who used WKWebView for awhile and don't want the origin change that comes when migrating to schemes.

Let me know if this helps or if you have any further questions.

chrnM commented 1 year ago

addHeader("Access-Control-Allow-Origin", ""); setHeader("Access-Control-Allow-Origin", ""); setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); setHeader("Access-Control-Max-Age", "3600"); setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, x-requested-with "); setHeader("Server", srvName); setHeader("X-Powered-By", mode); addHeader("X-FRAME-OPTIONS", mode); setHeader("X-Frame-Options", mode);

above headers using for server side and used XHR fix plugin but still not able get response from server and store data

breautek commented 1 year ago

The Access-Control-Allow-Origin should have a wildcard value of *, or a specific origin set (e.g. https://localhost)

I'll post a NGINX config that I use, and then I'll explain the bits so that you can add it to your own technology stack, whatever what that might be. I think most of this you have right based on your comment above, but I think you might be missing handling OPTIONS request, which I'll explain at the end.

add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' always;
add_header 'Access-Control-Allow-Headers' 'Accept, X-TP-META, X-BT-AUTH, Content-Type, X-Requested-With, JSNLog-RequestId, Access-Control-Allow-Origin, X-TP-SOURCE-TARGET, X-TP-SOURCE-VERSION' always;
if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' $http_origin always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' always;
        add_header 'Access-Control-Allow-Headers' 'Accept, X-TP-META, X-BT-AUTH, Content-Type, X-Requested-With, JSNLog-RequestId, Access-Control-Allow-Origin, X-TP-SOURCE-TARGET, X-TP-SOURCE-VERSION' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Content-Type' 'text/plain charset=UTF-8' always;
        add_header 'Content-Length' 0 always;
        return 204;
}

add_header 'Access-Control-Allow-Origin' $http_origin always;

This is setting the Access-Control-Allow-Origin response header to the same value as the request's Origin header. All CORs enabled browsers will send a Origin header in their request. This should be effectively the same as setting the Access-Control-Allow-Origin to * wildcard. The reason why I do it this way is because earlier versions (pre iOS 11 if I recall correctly) didn't work properly with *.

add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' always;

At the very minimum, you need OPTIONS inside Access-Control-Allow-Methods, but you'll probably want GET, and POST, and maybe a few others. It depends on what you use for server side rest APIs. OPTIONS are required because some APIs may implicitly send an OPTIONS request to the server before sending the actual request. You won't see this in the JS console, but you will see these requests on the server side.

What you have should be sufficient if all you use for HTTP methods are GET, POST, and DELETE.

add_header 'Access-Control-Allow-Headers' 'Accept, X-TP-META, X-BT-AUTH, Content-Type, X-Requested-With, JSNLog-RequestId, X-TP-SOURCE-TARGET, X-TP-SOURCE-VERSION' always;

Access-Control-Allow-Headers controls which headers are acceptable. I have a lot of things that are specific to my own projects, but the minimum you'll probably need Accept, Content-Type. If you use custom headers, you'll need to include them as well.

if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' $http_origin always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' always;
        add_header 'Access-Control-Allow-Headers' 'Accept, X-TP-META, X-BT-AUTH, Content-Type, X-Requested-With, JSNLog-RequestId, Access-Control-Allow-Origin, X-TP-SOURCE-TARGET, X-TP-SOURCE-VERSION' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Content-Type' 'text/plain charset=UTF-8' always;
        add_header 'Content-Length' 0 always;
        return 204;
}

This is the part that might be missing in your configuration. CORS have what's called a preflight request, which is a request that gets implicitly sent as a OPTIONS first, before the real request goes out. You won't see the OPTIONS request in the netwrok tab of the web inspector, but you'll see the request on the server side. The OPTIONS request must respond with the appropriate CORS headers like any other request, and must have no content body. The above NGINX configuration makes it so that if the incoming request is a OPTIONS request, it does just that. Sets the headers, including the Content-Length 0, and returns an http status 204.

Preflights aren't always used, they are used in certain circumstances. MDN Docs have more information on preflighted requests including when they are actually used.

chrnM commented 1 year ago

Thank you breautek now Its Working.