fetchは、HTTP呼び出しで標準的に使っていますが、Content-Typeに従った呼び出し方をいつも忘れてしまうので、備忘録として残しておきます。
実動作の確認のためのソースコードを以下のGitHubに上げておきました。
https://github.com/poruruba/fetch_laboratory
Javascriptからの呼び出しを中心に示しますが、同じコードがそのままNode.jsでも動作します。
ちょっとだけ、Lambdaでの動作も示しておきます。
呼び出し方法の種類
今回扱う呼び出し方法は以下の通りです。
・Get呼び出し
HTMLのページ取得でおなじみです。パラメータをQueryStringに指定します。例えば、以下のような呼び出しです。
http://localhost:10080/api?param1=abcd
・Post(Content-Type: application/json)呼び出し
パラメータをBody部にJSONで指定するWebAPI呼び出しでは一番一般的ですね。
・Post(Content-Type: application/x-www-form-urlencoded)呼び出し
フォームでSumitするときの方法です。例えば、以下のような呼び出しです。
<form action="http://localhost:10080/post-urlencoded" method="post">
・Post(Content-Type: multipart/form-data)呼び出し
フォームでmultipart指定でSumitするときの方法です。例えば、以下のような呼び出しです。
<form action="http://localhost:10080/post-formdata" method="post" enctype="multipart/form-data">
Javascriptの場合
まずは、Javascriptから。
Post(Content-Type: application/json)呼び出し
function do_post(url, body) {
const headers = new Headers({ "Content-Type": "application/json" });
return fetch(url, {
method: 'POST',
body: JSON.stringify(body),
headers: headers
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
});
}
Post(Content-Type: application/x-www-form-urlencoded)呼び出し
function do_post_urlencoded(url, params) {
const headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
var body = new URLSearchParams(params);
return fetch(url, {
method: 'POST',
body: body,
headers: headers
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
})
}
Post(Content-Type: multipart/form-data)呼び出し
function do_post_formdata(url, params){
var body = Object.entries(params).reduce( (l, [k,v])=> { l.append(k, v); return l; }, new FormData());
return fetch(url, {
method : 'POST',
body: body,
})
.then((response) => {
if( !response.ok )
throw new Error('status is not 200');
return response.json();
});
}
GET呼び出し
function do_get(url, qs) {
var params = new URLSearchParams(qs);
var params_str = params.toString();
var postfix = (params_str == "") ? "" : ((url.indexOf('?') >= 0) ? ('&' + params_str) : ('?' + params_str));
return fetch(url + postfix, {
method: 'GET',
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
});
}
まとめ
まとめるとこんな感じ
// input: url, method, qs, body, params, response_type, content_type, token, api_key
async function do_http(input){
const method = input.method ? input.method : "POST";
const content_type = input.content_type ? input.content_type : "application/json";
const response_type = input.response_type ? input.response_type : "json";
const headers = new Headers();
if( content_type != "multipart/form-data" )
headers.append("Content-Type", content_type);
if( input.token )
headers.append("Authorization", "Bearer " + input.token);
if( input.api_key )
headers.append("x-api-key", input.api_key);
let body;
if( content_type == "application/json" ){
body = JSON.stringify(input.body);
}else if( content_type == "application/x-www-form-urlencoded"){
body = new URLSearchParams(input.params);
}else if( content_type == "multipart/form-data"){
body = Object.entries(input.params).reduce((l, [k, v]) => { l.append(k, v); return l; }, new FormData());
}
const params = new URLSearchParams(input.qs);
var params_str = params.toString();
var postfix = (params_str == "") ? "" : ((input.url.indexOf('?') >= 0) ? ('&' + params_str) : ('?' + params_str));
return fetch(input.url + postfix, {
method: method,
body: body,
headers: headers
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
if( response_type == "json" )
return response.json();
else if( response_type == 'blob')
return response.blob();
else if( response_type == 'file'){
const disposition = response.headers.get('Content-Disposition');
let filename = "";
if( disposition ){
filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
if (filename.toLowerCase().startsWith("utf-8''"))
filename = decodeURIComponent(filename.replace(/utf-8''/i, ''));
else
filename = filename.replace(/['"]/g, '');
}
return response.blob()
.then(blob =>{
return new File([blob], filename, { type: blob.type })
});
}
else if( response_type == 'binary')
return response.arrayBuffer();
else // response_type == "text" )
return response.text();
});
}
Node.jsの場合
Node.jsでも呼び出し方は同じです。
ポイントは、以下のnpmモジュールを利用するところです。
・node-fetch
https://github.com/node-fetch/node-fetch
・form-data
https://github.com/form-data/form-data
以下を先頭に記述することで、ほぼJavascriptと同じ記述で同じ動作となります。
const FormData = require('form-data');
const { URL, URLSearchParams } = require('url');
const fetch = require('node-fetch');
const Headers = fetch.Headers;
実験のためのサンプルページ
以下のようなページから、各呼び出し方法を確認します。
F11を押して、DevToolsを開くと、通信内容を詳しく見ることができます。
先ほど示した関数群を呼び出しているだけでして、詳細はGitHubのソースコードを参照ください。
参考までに、リクエストを受け付けるサーバ側のソースも示しておきます。
'use strict';
const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';
const Response = require(HELPER_BASE + 'response');
const Redirect = require(HELPER_BASE + 'redirect');
// Lambda+API Gatewayの場合に必要
//const { URLSearchParams } = require('url');
//const multipart = require('aws-lambda-multipart-parser');
exports.handler = async (event, context, callback) => {
if( event.path == '/post-json'){
console.log(event.body);
var body = JSON.parse(event.body);
var response = {
path : event.path,
param: {
param1: body.param1,
param2: body.param2,
}
};
return new Response(response);
}else
if( event.path == '/post-urlencoded'){
// Lambda+API Gatewayの場合はこちら
//var body = {};
//for( var pair of new URLSearchParams(event.body).entries() ) body[pair[0]] = pair[1];
// swagger_nodeの場合はこちら
var body = JSON.parse(event.body);
var response = {
path : event.path,
param: {
param1: body.param1,
param2: body.param2,
}
};
return new Response(response);
}else
if( event.path == '/post-formdata' ){
// Lambda+API Gatewayの場合はこちら
//var body = multipart.parse(event);
// swagger_nodeの場合はこちら
var body = JSON.parse(event.body);
console.log(body);
var response = {
path : event.path,
param: {
param1: body.param1,
param2: body.param2,
}
};
return new Response(response);
}else
if( event.path == '/get-qs'){
console.log(event.queryStringParameters);
var response = {
path : event.path,
param: {
param1: event.queryStringParameters.param1,
param2: event.queryStringParameters.param2,
}
};
return new Response(response);
}
};
一方で、Node.jsでの呼び出しも確認したかったので、Node.js側の確認のためのソースも示しておきます。
動作としては、いったん、サーバ側でリクエストを受け付けたのち、パラメータで指定されたbody.typeの値によって、各呼び出し方法に従った呼び出しをNode.jsで行っています。
コードを見ていただくと、Javascriptでの呼び出しと同じであることがわかります。
'use strict';
const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';
const Response = require(HELPER_BASE + 'response');
const Redirect = require(HELPER_BASE + 'redirect');
const FormData = require('form-data');
const fetch = require('node-fetch');
const { URL, URLSearchParams } = require('url');
const Headers = fetch.Headers;
const baseurl = 'http://localhost:10080';
exports.handler = async (event, context, callback) => {
if( event.path == '/node'){
console.log(event.body);
var body = JSON.parse(event.body);
if( body.type == "post-json"){
var params = {
param1: body.param1,
param2: body.param2,
};
return do_post(baseurl + '/post-json', params)
.then(json => {
console.log(json);
return new Response({ type: body.type, resposne: json });
})
.catch(error => {
console.log(error);
return new Response(error);
});
}else
if( body.type == "post-urlencoded"){
var params = {
param1: body.param1,
param2: body.param2,
};
return do_post_urlencoded(baseurl + '/post-urlencoded', params)
.then(json => {
console.log(json);
return new Response({ type: body.type, resposne: json });
})
.catch(error => {
console.log(error);
return new Response(error);
});
}else
if( body.type == "post-formdata"){
var params = {
param1: body.param1,
param2: body.param2,
};
return do_post_formdata(baseurl + '/post-formdata', params)
.then(json => {
console.log(json);
return new Response({ type: body.type, resposne: json });
})
.catch(error => {
console.log(error);
return new Response(error);
});
}else
if( body.type == 'get-qs'){
var qs = {
param1: body.param1,
param2: body.param2,
}
return do_get(baseurl + '/get-qs', qs)
.then(json => {
console.log(json);
return new Response({ type: body.type, resposne: json });
})
.catch(error => {
console.log(error);
return new Response(error);
});
}
var response = {
type : body.type,
param: {
param1: body.param1,
param2: body.param2,
}
};
return new Response(response);
}
};
function do_post(url, body) {
const headers = new Headers({ "Content-Type": "application/json; charset=utf-8" });
return fetch(url, {
method: 'POST',
body: JSON.stringify(body),
headers: headers
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
});
}
function do_get(url, qs) {
var params = new URLSearchParams(qs);
return fetch(url + `?` + params.toString(), {
method: 'GET',
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
});
}
function do_post_urlencoded(url, params) {
const headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
var body = new URLSearchParams(params);
return fetch(url, {
method: 'POST',
body: body,
headers: headers
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
})
}
function do_post_formdata(url, params) {
var body = Object.entries(params).reduce((l, [k, v]) => {
l.append(k, v);
return l;
}, new FormData());
return fetch(url, {
method: 'POST',
body: body,
})
.then((response) => {
if (!response.ok)
throw new Error('status is not 200');
return response.json();
});
}
補足
※レスポンスの型
最近レスポンスはJSONで返されることが多くなっていますので、すべてJSONで返ってくることを想定しています。もし、単なるテキストで返ってくる場合には以下の部分を変更します。
(変更前) return response.json();
→
(変更後) return response.text();
以上