AFNetworkingのAFHTTPSessionManagerを使っていてBodyに素のXMLを入れてPOSTしたかったのだが、そのやり方がなかなか見当たらずソースコードを読んで解決したのでメモしておく。

ざっくり言うとAFHTTPSessionManagerでのPOSTはこんな感じに行う。

1let manager = AFHTTPSessionManager()
2manager.POST(url, parameters: params, success: { (task, response) -> Void in
3    // success
4}) { (task, error) -> Void in
5    // failure
6}

POSTするパラメータがあれば、上のメソッドのparametersのところにdictionaryとかで渡せばいいはずだが(確か)、今回やりたいのはBodyにXMLのテキストを入れるということだった。

まず気軽にparametersにXML文字列を直接渡してみようなどとやってみたら失敗した。

1let manager = AFHTTPSessionManager()
2let xmlstr = "<?xml ..."
3manager.POST(url, parameters: xmlstr, success: { (task, response) -> Void in
4    // success
5}) { (task, error) -> Void in
6    // failure
7}

これでできたリクエストを見てみるとBodyにはこんなのが入っていた。

(null)=%3C%3Fxml%20...

どうやら名前が空で値がURLエンコード済文字列のパラメータをBodyとして設定してしまっているらしい。

ソースコードを読むとそれがデフォルトの動作だということがわかった。

1// AFURLRequestSerialization.m
2switch (self.queryStringSerializationStyle) {
3    case AFHTTPRequestQueryStringDefaultStyle:
4        query = AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding);
5        break;
6}

じゃあデフォルトじゃない動作は、というと上のコードの近くにあった。

 1// AFURLRequestSerialization.m
 2if (self.queryStringSerialization) {
 3    NSError *serializationError;
 4    query = self.queryStringSerialization(request, parameters, &serializationError);
 5
 6    if (serializationError) {
 7        if (error) {
 8            *error = serializationError;
 9        }
10
11        return nil;
12    }
13}

self.queryStringSerializationはBlockで、このブロックを設定してparameterのシリアライズをカスタマイズできるよ、ということのようだった。

つまり文字列をそのままBodyに入れたい場合は、

 1let manager = AFHTTPSessionManager()
 2let xmlstr = "<?xml ..."
 3
 4manager.requestSerializer.setQueryStringSerializationWithBlock { (request, params, error) -> String! in
 5    return params as! String
 6}
 7
 8manager.POST(url, parameters: xmlstr, success: { (task, response) -> Void in
 9    // success
10}) { (task, error) -> Void in
11    // failure
12}

manager.requestSerializer.setQueryStringSerializationWithBlockでparameterをそのまま文字列と返してあげればよかった。

これで解決できた。良かった。