25

为此,我将假装原始 url 是http://host/form,而新 url 是https://host/form. (请注意,在我发布这个之前,两个 URL 都将是安全的。但是,非安全到安全似乎是一个方便的重定向来测试它。)

我正在使用NSURLConnection重定向我的 Web API 访问。基本上,我想将我刚刚提交的所有内容http://hostaform重新提交给https://host/form. 我认为这将是默认行为,但看起来身体在重定向中丢失了。

所以我认为我需要处理 's 代表的connection:willSendRequest:redirectResponse:事件NSURLConnection并重新附加身体。问题是这条消息似乎没有充分记录。我可以在此方法上找到的唯一信息是NSURLConnection 类参考,这不是很有帮助。除其他外,它包括:

redirectResponse:导致重定向的 URL 响应。如果由于代理参与重定向处理而未发送此方法,则可能为 nil。

我不确定这意味着什么。结合初始willSendRequest:调用,我认为这意味着willSendRequest:在重定向响应之前,即使是我的初始请求也正在发送。那是对的吗?

因此,我向我的委托添加了代码以额外保留正文,并添加了以下willSendRequest:处理程序:

- (NSURLRequest *)connection: (NSURLConnection *)inConnection
             willSendRequest: (NSURLRequest *)inRequest
            redirectResponse: (NSURLResponse *)inRedirectResponse;
{
    if (inRedirectResponse) {
        NSMutableURLRequest *r = [[inRequest mutableCopy] autorelease];
        [r setURL: [inRedirectResponse URL]];
        [r setHTTPBody: body];
        return r;
    } else {
        return inRequest;
    }
}

它不起作用。但我什至不确定这是否是正确的方法。这对我来说似乎过于hackish。我应该做什么?这在任何地方都有记录吗?到目前为止,我在 Apple 的文档或使用 Google 中没有发现任何有用的信息。

(这是在 iPhone 上,尽管这些类似乎没有太大区别。)

4

4 回答 4

38

RFC 2616 的第 10.3.2 节中有一条关于此行为的注释:

注意:当收到 301 状态码后自动重定向 POST 请求时,一些现有的 HTTP/1.0 用户代理会错误地将其更改为 GET 请求。

所以这种行为似乎是非标准的,但却是历史性的。该GET请求不是 a POST,它将丢失有效负载。

有趣的是,这也在同一部分:

如果收到 301 状态代码以响应 GET 或 HEAD 以外的请求,除非用户可以确认,否则用户代理不得自动重定向请求,因为这可能会改变发出请求的条件。

这很清楚,似乎表明我们无法解决这个问题,但我认为为了我们自己的 Web 服务客户端选择(或控制)服务而忽略这一点可能是最不坏的选择。

那么我们如何解决这个问题呢?

而不是willSendResponse:原来的问题,我使用这个:

- (NSURLRequest *)connection: (NSURLConnection *)connection
             willSendRequest: (NSURLRequest *)request
            redirectResponse: (NSURLResponse *)redirectResponse;
{
    if (redirectResponse) {
        // we don't use the new request built for us, except for the URL
        NSURL *newURL = [request URL];
        // Previously, store the original request in _originalRequest.
        // We rely on that here!
        NSMutableURLRequest *newRequest = [_originalRequest mutableCopy];
        [newRequest setURL: newURL];
        return newRequest;
    } else {
        return request;
    }
}

这里的想法是,我不是克隆新请求并尝试将其塑造成与 Cocoa Touch 发送给我的请求相同,而是创建原始请求的克隆并仅更改 URL 以匹配 Cocoa Touch 发送给我的请求。那个原始请求仍然是一个POST附加了有效负载的请求。

如果您控制服务器,则值得阅读RFC 2616 第 10.3 节的全文,看看是否有更好的代码可以使用(当然,还要检查 iOS 是否按应有的方式处理更好的代码)。

您还可以制作重定向请求的可变副本,并将其 HTTP 方法替换为原始请求的 HTTP 方法。相同的一般原则,尽管这有利于保留新请求而不是旧请求。在某些情况下可能会更好,但我尚未对此进行测试。

于 2009-09-18T20:13:23.380 回答
13

您应该检查服务器发送的 HTTP 响应状态代码,以确定是发送 GET 还是重复 POST。对于 303(或 302),发送 GET 请求。对于 307,重复 POST。

于 2010-08-03T04:01:52.677 回答
4

我在重定向时遇到了同样的问题。感谢 AJSoaks!我按照他的建议尝试了,问题解决了。

所以,我试图通过 POST 方法发布用户名和密码,我看到服务器重定向了我的请求。正如 AJSoaks 所说,如果出现 302 错误,您应该重复请求,但这次使用 GET 方法而不是之前的 POST。

...在某些时候,您有以下几行:...如果您的 IBAction(按下按钮)方法或您想要的任何地方,它可以在里面...

NSMutableString *postString = [[NSMutableString alloc] init];

[postString appendString:@"username=YourUsername&password=YourPassword"];

    //the original URL (https means that it supports SSL protocol)
    //it doesn't change anything, don't worry about it
NSURL *URL = [NSURL URLWithString:@"https://loginWebpageURL"];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];

[request setHTTPMethod:@"POST"];    
[request setValue:[NSString stringWithFormat:@"%d", [postString length]] forHTTPHeaderField:@"Content-length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

[NSURLConnection connectionWithRequest:request delegate:self];

[postString release];
[request release];

比你还应该实现重定向 NSURLConnection 委托方法,具有以下签名:

- (NSURLRequest *)connection:(NSURLConnection *)connection
            willSendRequest:(NSURLRequest *)request
           redirectResponse:(NSURLResponse *)redirectResponse

在此方法中,如果您有 SERVER 的错误 302 或 303,您应该实现类似于下面的代码,只需复制您看到的代码并将其替换为新的 URL(重定向)。您可以在浏览器中看到的新 URL,或者如果您想要它非常有用,将来也可以使用 Firebug(Firefox 插件)或 Safari WEB INSPECTOR 检查它。如果您使用 Firebug,您可以在“Net”选项下找到所有信息:

if (redirectResponse) {

    NSLog(@"REDIRECT");
    NSMutableURLRequest *requestTmp = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://areaclienti.tre.it/selfcare/areaclienti133/4552_infoCosti_ITA_HTML.xsl"]];

    return [requestTmp autorelease];
}

//return original request in case thay there is no redirecting...
else return request;
于 2011-03-06T16:16:04.210 回答
0

NSURLConnection 不会将 originalRequest 标头添加到“willSendRequest: (NSURLRequest *)inRequest”中的重定向请求中。

您可以通过将“originalRequest.headers”添加到重定向请求中来解决此问题。

于 2014-04-01T00:21:07.120 回答