はじめに
NSURLConnectionを使ったコーディングの演習に、googleのURLを叩いたら必ず落ちる、というところから対応方法を検討しました。
確認
文字コードのエンコーディング部分で落ちていることが分かり
let responseStr:NSString = NSString(data:resultResponseData, encoding:NSUTF8StringEncoding)!
SafariでのWebインスペクタを見てみました。
リソースで確認してみたところ
- charsetはUTF-8
- Content-Encodingはgzip
HeaderFieldを追加
// Do any additional setup after loading the view.
let url:NSURL = NSURL(string:"http://google.co.jp")!
var request:NSMutableURLRequest = NSMutableURLRequest(URL:url)
request.addValue("utf-8", forHTTPHeaderField: "Accept-Charset")
request.addValue("gzip", forHTTPHeaderField: "Accept-Encoding")
Accepts-Encoding → Accept-Encodingに書き換えました
結果は変らずで落ちました。
返ってくるresponseのヘッダは確認できます。結果に変化がなかったので、上記対応で良いのかは疑問が残ります
response headerを見る
NSURLConnection.sendAsynchronousRequest(
request,
queue: NSOperationQueue.mainQueue(),
completionHandler: {
(response:NSURLResponse?, responseData:NSData?, error: NSError?) -> Void in
if let resultError = error
{
println(resultError.description)
}
if let httpResponse = response as? NSHTTPURLResponse {
let headers:NSDictionary = httpResponse.allHeaderFields
println("textEncodingName : \(httpResponse.textEncodingName) ");
println("----------------------------------------");
for obj in headers{
println("key : \(obj.key) value : \(obj.value)");
}
println("----------------------------------------");
let statusCode = httpResponse.statusCode
println("MIMEType : \(httpResponse.MIMEType)")
if statusCode != 200 {
println("sendAsynchronousRequest status code = \(statusCode); response = \(response)")
}
}
})
結果
Shift_JISが返ってきていました。gzipもされていない。
サーバ側で受け取ったリクエスト
自前サーバのphpinfo()で取得した内容から
"Accept-Encoding"と"Accepts-Encoding"があるのは送信時に間違ったからですが、特に影響はないようでした。
<tr><td class="e">_ENV["HTTP_HOST"]</td><td class="v">f-y.sakura.ne.jp</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPTS_ENCODING"]</td><td class="v">gzip</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT_CHARSET"]</td><td class="v">utf-8</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT"]</td><td class="v">*/*</td></tr>
<tr><td class="e">_ENV["HTTP_USER_AGENT"]</td><td class="v">[プロジェクト名]/1 CFNetwork/711.1.12 Darwin/13.4.0</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT_LANGUAGE"]</td><td class="v">en-us</td></tr>
<tr><td class="e">_ENV["HTTP_ACCEPT_ENCODING"]</td><td class="v">gzip, deflate</td></tr>
<tr><td class="e">_ENV["SERVER_PROTOCOL"]</td><td class="v">HTTP/1.1</td></tr>
対応
headerのtextEncodingNameに自動で対応させるように変更。
実装例
// Do any additional setup after loading the view.
let url:NSURL = NSURL(string:"http://yahoo.co.jp")!
var request:NSMutableURLRequest = NSMutableURLRequest(URL:url)
NSURLConnection.sendAsynchronousRequest(
request,
queue: NSOperationQueue.mainQueue(),
completionHandler: {
(response:NSURLResponse?, responseData:NSData?, error: NSError?) -> Void in
if let resultError = error
{
println(resultError.description)
}
//Converting data to String
var encoding: UInt = NSUTF8StringEncoding
if let httpResponse = response as? NSHTTPURLResponse {
let headers:NSDictionary = httpResponse.allHeaderFields
println("textEncodingName : \(httpResponse.textEncodingName) ");
if let textEncName = httpResponse.textEncodingName {
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(textEncName))
if encoding == UInt(kCFStringEncodingInvalidId) {
encoding = NSUTF8StringEncoding; // by default
}
}
println("----------------------------------------");
for obj in headers{
println("key : \(obj.key) value : \(obj.value)");
}
println("----------------------------------------");
let statusCode = httpResponse.statusCode
println("MIMEType : \(httpResponse.MIMEType)")
if statusCode != 200 {
println("sendAsynchronousRequest status code = \(statusCode); response = \(response)")
}
}
if let resultResponseData = responseData {
var stringEncoding : NSStringEncoding = NSUTF8StringEncoding
if encoding != UInt(kCFStringEncodingInvalidId){
let responseStr:NSString = NSString(data:resultResponseData, encoding:encoding)!
// let responseStr:NSString = NSString(data:resultResponseData, encoding:NSUTF8StringEncoding)!
println(responseStr)
} else {
println("Content-Typeのcharsetを確認")
}
}
Content Negotiationについて
参考URL
[Why] NSString の initWithData:encoding: で NSShiftJISStringEncoding を設定して nil となる.
https://hirooka.pro/?p=6205
6.1 HTTPリクエストの基礎
https://github.com/mixi-inc/iOSTraining/wiki/6.1-HTTPリクエストの基礎
コンテントネゴシエーション
http://httpd.apache.org/docs/2.2/content-negotiation.html
さいごに
request時のacceptヘッダ系で都合の良いものを返してくれそうな気がしていたのですが
方法が見つからず(気のせいな可能性もあり)アプリ側をサーバの言いなりに合わせた対応になったのが納得がいかない部分です。
コンテンツネゴシエーションの対応は普通なのかと思ったら、そうでもなく。
Webpで使っているのを見るくらいです。
サーバ側を自前に変えての実装も別途調整してみます。
サンプルを公開してあるサイトの文字列エンコーディングにはNSUTF8StringEncoding
とあることが多く、暗黙の概念化していました。
またNSShiftJISStringEncoding
について調べると、CP932とかガラケー時代の懐かしの文字コードが見つかって現役なのに驚きました。
でも、googleがShiftJISで返している理由は、分からないまま・・・。