概要
以前こんな記事を書きました。
JavaScript上でObjectをRailsで取得できる形のFormDataへ変換する
https://qiita.com/dorarep/items/f3aa1463123c1ae75879
今回、似たような形でNode.js上で動くサーバサイド側へ楽にデータを受け渡ししたいので、クライアント側、サーバサイドの変換ロジックをTypeScriptで実装しました。
実装
クライアント側
/**
* Convert object to FormData which backend can use.
* This function is useful for uploading files.
*
* ex)
* { id: 1, hero: { id: 1, name: 'NewHero' }, items: [1, 2] }
* -> FormData with following parameters
* id: 1
* hero.id: 1
* hero.name: NewHero
* items.: 1
* items.: 2
*
* @param params
* @return FormData
*/exportconstmakeFormDataFromParams=(params:object):FormData=>{constformData=newFormData();constappendParamsToForm=(variable,prefix='')=>{if(typeofvariable!=='object'||variableinstanceofFile){formData.append(prefix,variable);return;}if(Array.isArray(variable)){variable.forEach(value=>appendParamsToForm(value,`${prefix}.`));return;}Object.keys(variable).forEach(key=>{appendParamsToForm(variable[key]||'',prefix?`${prefix}.${key}`:key);});};appendParamsToForm(params);returnformData;};
サーバサイドはformidable
を使用します。
/**
* ex)
* id: 1
* hero.id: 1
* hero.name: NewHero
* items.: 1
* items.: 2
* -> { id: 1, hero: { id: 1, name: 'NewHero' }, items: [1, 2] }
*
* @param req
* @return object
*/exportconstconvertRequestToObject=async(req)=>{const{IncomingForm}=require('formidable');consthash=awaitnewPromise((resolve,reject)=>{constform=newIncomingForm()form.parse(req,(err,fields,files)=>{if(err)returnreject(err)resolve({...fields,...files})})})returnObject.entries(hash).reduce((current,[path,value])=>mergeDeep(current,buildObject(path,value)),{})}constbuildObject=(path:string,value:any):object=>path.split('.').reverse().reduce((current,path)=>{if(path===''){return[current]}else{return{[path]:current}}},value);constmergeDeep=(target,source)=>{constisObject=(obj)=>obj&&typeofobj==='object';if(!isObject(target)||!isObject(source)){returnsource;}Object.keys(source).forEach(key=>{consttargetValue=target[key];constsourceValue=source[key];if(Array.isArray(targetValue)&&Array.isArray(sourceValue)){target[key]=targetValue.concat(sourceValue);}elseif(isObject(targetValue)&&isObject(sourceValue)){target[key]=mergeDeep(Object.assign({},targetValue),sourceValue);}else{target[key]=sourceValue;}});returntarget;}
ref) mergeDeep
https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6#gistcomment-3120712