【Angular4】urlencoded用にobjectを変換
Angular4にてHttpリクエストをJSONではなくurlencodedで行おうとしたときに、Objectがnestしている場合にうまくいきませんでした。
順を追って記載しますが、お急ぎの方は最終的なコードが記事の下部にありますのでそちらからどうぞ。
さて、本題です。
まずはリクエストパラメータの基本的な指定方法です。
const data = { firstName: 'taro', lastName: 'yamada', age: 30 }; const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }); let params = new HttpParams(); params = params.set('firstName', data.firstName); params = params.set('lastName', data.lastName); params = params.set('age', data.age);
パラメータを個別にsetしていくわけですね。
これを動的にするために以下のようにしてみました。
const data = { firstName: 'taro', lastName: 'yamada', age: 30 }; const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }); let params = new HttpParams(); for (var key in data) { params = params.set(key, data[key]); }
しかし、これではオブジェクトの中にオブジェクトがあったり、配列があったり、オブジェクトが入れ子になっている場合には対応できません。
例えば、以下のようなオブジェクト構造です。
const data = { firstName: 'taro', lastName: 'yamada', age: 30, job: [ { name: 'first-company', join: '2015-04-01' }, { name: 'second-company', join: '2018-04-01' } ], skill: ['skill1', 'skill2', 'skill3'] };
こういった構造の時、前述したコードではパラメータが [object object] という風に設定されてしまいました。
オブジェクトの構造が可変でも、汎用的に利用できるようにしたいところですね。
「サンプルコードはないかな」とJSONデータをURLEncodedに変換するコードを調べてみたところ、ちょうどいいコードがGitHubに公開されていました。
function JSON_to_URLEncoded(element,key,list){ var list = list || []; if(typeof(element)=='object'){ for (var idx in element) JSON_to_URLEncoded(element[idx],key?key+'['+idx+']':idx,list); } else { list.push(key+'='+encodeURIComponent(element)); } return list.join('&'); }引用元: JSON_to_URLEncoded.js
ははぁん。なるほど。関数を再帰的に利用するやーつ。
参考にさせていただきましょう。
// TSファイル setHttpParams(element, httpParams: HttpParams, key?) { if (typeof(element) == 'object') { for (var idx in element) { httpParams = this.setHttpParams(element[idx], httpParams, key ? key + '[' + idx + ']' : idx); } } else { httpParams = httpParams.set(key, element); } return httpParams; }
これでオブジェクトの構造がネストしてても対応できます。
呼び出すときには以下のような形になります。
const data = { // 素敵なオブジェクト } const reqOpts = { headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }) }; let params = new HttpParams(); params = this.setHttpParams(data, params); this.http.post('https://~~~', params, reqOpts);
前述した入れ子になったオブジェクトを変換すると、以下のような形になります。
firstName: 'taro', lastName: 'yamada', age: 30, job[0][name]: 'first-company', job[0][join]: '2015-04-01', job[1][name]: 'second-company', job[1][join]: '2018-04-01', skill[0]: 'skill1', skill[1]: 'skill2', skill[2]: 'skill3'