I want to do a POST request to my WKWebView
but the headers doesn\'t get set when I monitor the requests with Charles so the request fails. What is wrong here?<
This appears to be a bug.
https://bugs.webkit.org/show_bug.cgi?id=140188
Hopefully it will be addressed soon. In the meantime, reverting to UIWebView or implementing the workaround proposed by Spas Bilyarski in his answer seems to be the best options.
I had the same problem with WKWebView, that I decided to use instead of UIWebView to avoid the pickers crash in iOS 8. There are two ways that I can think of:
connection:didReceiveData:
and connectionDidFinishLoading:
from the delegate if you don't use a self signed SSL certificate)Create file eg. "POSTRequestJS.html":
<html>
<head>
<script>
//POST request example:
//post('URL', {key: 'value'});
function post(path, params) {
var method = "post";
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
</script>
</head>
<body>
</body>
</html>
And in your code after where you want to load your request:
NSString *path = [[NSBundle mainBundle] pathForResource:@"POSTRequestJS" ofType:@"html"];
NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
WKWebView.navigationDelegate = self;
[WKWebView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
Add method:
- (void)makePostRequest
{
NSString *postData = [NSString stringWithFormat: @"email=%@&password=%@", email, password];
NSString *urlString = @"http://materik.me/endpoint";
NSString *jscript = [NSString stringWithFormat:@"post('%@', {%@});", urlString, postData];
DLog(@"Javascript: %@", jscript);
[WKWebView evaluateJavaScript:jscript completionHandler:nil];
didMakePostRequest = YES;
}
And last add the WKNavigationDelegate:
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
if (!didMakePostRequest) {
[self makePostRequest];
}
}
workaround: trick by using html5 & javascript.
Add a html5 file with content below to your xcode project. To post data by using javascript & h5 form:
<html>
<head>
<script>
//how to call: post('URL', {"key": "value"});
function post(path, params) {
var method = "post";
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
</script>
</head>
<body>
</body>
</html>
Load the h5 file to WKWebView:
WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init];
config.preferences = [[WKPreferences alloc]init];
config.preferences.javaScriptEnabled = YES;
WKWebView* webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
webView.navigationDelegate = self;
[self.view addSubview:webView];
NSString *path = [[NSBundle mainBundle] pathForResource:@"JSPOST" ofType:@"html"];
NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
Prepare the parameters to post. ie. a string & an array of dictionary Note: when turn array to json string by using NSJSONSerialization, '\r' may be added automaticly. You must remove all the '\r' in the json string, or the javascript cannot be parsed correctly.
// parameters to post
NSString* name = @"Swift";
NSArray* array = @[@{@"id":@"1", @"age":@"12"}, @{@"id":@"2", @"age":@"22"}];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:array options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\'"];
// trim spaces and newline characters
jsonString = [jsonString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
NSString *postData = [NSString stringWithFormat: @"'name':'%@', 'contacts':'%@'", name, jsonString];
// page url to request
NSString *urlStr = @"http:api.example.com/v1/detail";
// javascript to evalute
NSString *jscript = [NSString stringWithFormat:@"post('%@',{%@});", urlStr, postData];
//NSLog(@"Javzascript: %@", jscript);
Put this in the WKWebView's delegate: didFinishNavigation
// call the javascript in step 3
(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
GCD_MAIN((^{
[_web evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
if (error) {
NSLog(@"----------->>>>>>>>>>>>> evaluateJavaScript error : %@", [error localizedDescription]);
}
}];
}));
}
I use this delegate method and it works !!!
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"%@",navigationAction.request.allHTTPHeaderFields);
NSString *accessToken = @"Bearer 527d3401f16a8a7955aeae62299dbfbd";
NSMutableURLRequest *request = [navigationAction.request mutableCopy];
if(![[request.allHTTPHeaderFields allKeys] containsObject:@"Authorization"]){
[request setValue:accessToken forHTTPHeaderField:@"Authorization"];
decisionHandler(WKNavigationActionPolicyCancel);
[Helper hideProgressHUD];
[webView loadRequest:request];
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
WKWebView.load
method doesn't work with post request with post body. You have to use JavaScript to do the trick, check WKWebView.evaluateJavascript
.
It maybe a bug, but Apple hasn't addressed it till now.
I can confirm this problem. A simple workaround for me was an AJAX request, with jQuery:
$.ajax({
type : 'POST',
url : $('#checkout-form').attr('action'),
data : $('#checkout-form').serialize()
}).done(function(response, status) {
// response if return value 200
}).fail(function(status, error) {
console.log(error);
});
where my form looks like
<form id="checkout-form" method="POST" action="/shop/checkout">
...
</form>
I hope this helps somebody...