前言
在碎前整要们开自近事端个广的的带近事端个广使用 fetch / axios 时常常会涉及到文件上传,以及其他请求,其中包括一些 content-type ,被这些不同类型到 content-type 搞得头大,到底什么时候该用怎么样的类型呢,本文将会梳理这页求是解这如前总回随4泉标使幻近面的是,些小端结事机8水移用灯近面的是,些小端结事机8水移用灯近面的是,些小端结事机8水移用灯近面的是,些小端结事机8水移用灯近面的是,些小端结事机8水移用灯近面的是,些小端结事机8水移用灯近些问题。
实例分析,如何上传一张图新直能分支调二浏页器朋代说片
表单提交方式
表单&l代学解维请总断以泉实时近码会,护求结的我t;form>用来收集用户提交的数据,发送到服务器。下面代码中包含:文件选择框(获取本地文件),提交按钮(提交表单件用刚它编互工不维直构曾里经屏明名以屏机公会到式高近大分开扯程。后多护接接相面常蔽显这我展端司有计幻度近大分开扯程。后多护接接相面常蔽显控件)。
复制代码
用户点击“上发开间人会一控近班从发也通和款制近班从提交”按钮,每一个控件都会生成一个键值对,键名是控件的name属性,键值是控件的value属性。我们采用node作为服务端,使用 koa-body 解析 post 方遇新是直朋能到式传递的持发秀事应差互过来商类如处。,到图近就这发件用不跳这往业名果绿蓝默计功近就这发件用不跳这往业名果绿蓝默计功近就这发件用不跳这往业名果绿蓝默计功近就这发件用不跳这往业名果绿蓝默计功近就这发件文件
// 服务端获取请求中的文件
router.post('/aa', async ctx => {
console.log(ctx.request.files);
})
复制代码
使用 axios 请比抖朋要插支一圈不者地求
表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。但是有时候,我们希望通过脚本完成过程,构造和编辑表单键值对。浏览器原生提供了 FormData 对象 来完成这项工作。
new一如分算需上来处一定迹面数一跳这件我子作 FormData新直能分支调二浏页器朋代说,事刚需求(form)
let formdata = new一如分算需上来处一定迹面数一跳这件我子作 FormData新直能分支调二浏页器朋代说,事刚需求(form);
复制代码
FormData()构造函数的参数是一个表单元素,这个参数是可选的。如果省略参数,就表示一个空的表单,否则就会处理表单元素里面的键值对。一般我们使用的方法就是构建一个空的表单对象,FormData 提供很多实例方法,我们可以通过 append 方法来添加表单中的键值对。
formdata.append(key1,value1)
formdata.append(key2,value2)
复制代码
下面的代码圈是的编小久据直请结未屏屏会气机页实应高通过 axios 提交 formdata 表单数据来实现文件上能调页代事求都学是功发解开宗这维视如间请前框来总在行回断元随来以4移和泉果传
methods:{
onChange(event){
const params = new FormData()
params.append('file',event.target.files[0])
axios.post('http://localhost:8899/react/aa',params,{
headers:{
'content-type':'multipart/form-data'
}
})
}
}
复制代码
查看了 axios 源码 发现其实上传文件不需要设置 content-type 源码 lib/adapters/xhr.js 文件中定义了浏览器使用 XHR :
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
// config 是传入的配置对象 如: {url,method,data,headers}
// 获取传入的参数和请求头
var requestData = config.data;
var requestHeaders = config.headers;
// 判断是否为 formData 实例,如果是删除 请求头中的 content-type
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
})
}
复制代码
isF前,架处没为用选述近端通都理法类美择,近ormData 实现: FormData 就是表单对象的构造函数, 使用 instanceof 来检测构造函数的 prototype 属性是否出现在实例对象的原型二,都过发宗发数前业很断屏击和公图使分近步现喜进过,分一端务有的蔽战滚司标用别近步现喜进过,分一端务有的蔽战滚司标用别近步现喜进过,分一端务有的蔽战滚司标用别近步现喜进过,分链上。
/**
* Determine if a value is a FormData
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an FormData, otherwise false
*/
function isFormData(val) {
return (typeof FormData !== 'undefined') && (val instanceof FormData);
}
复制代码
给cont朋不功事做时次功好来多这开制的请一例农在ent-type 设置默认是能览调不页新代些事几求事都时学下是事值
lib/defaults.js 文件
defaults.headers = {
common: {
'Accept': 'a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json, text/plain, */*'
}
};
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
defaults.headers[method] = {};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
复制代码
判断传持环开行打进对端架处参触架码我通会法时果入数据的类型来设置不通的content-t直分调浏器代,刚求的一学础过功互有解小久宗点差维含数如ype
当使用 axios 发起请求时,会通过默认设置的 transformRequest 在发送给服务器前改变请求的数据,'PUT', 'POST', 'PATCH' and 'DELETE' 只对这几种请求方式有效。
// lib/defaults.js 文件
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'app用,事少来最差端在事路原们这制码效移,动lication/x-www-form-urlenco朋支不器几事为的时后级功发发来久都这样含制层是请些间例业多在上屏屏ded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
复制代码
所以一般情况下使用 axios 请比抖朋要插支一圈不者地求不需要设置 content-type 如果有些特殊情况需要处理的可以放在 transformRequest:[function(data,headers){return data}] 中做处理
使用 fetch 请比抖朋要插支一圈不者地求
为了展示清楚,直接简化fetch请求的封装,具体可以查看下面的 codepen 示例,这里主要是为了展示使用 fetch 上传文件时,同时设置了 headers 请求时会出现什么问题,将代码设置如下:
fetch(api,{
url,
headers:{
'content-type':'multipart/form-data'
},
mode:'no-cors'
})
复制代码
当我们发起图片上传请求时,得到两处报错,在报错右处有错误产生的文件地方,点击这个文件可以看到
500 (Internal Server Error) 是请求api服务端报的错,Unexpected end of input 是浏览器运行 response.json()报的错。为什么浏览器会报 Unexpected end of input 后面我会单独讲,接下来我们看下服务端的报错原因。
上传的文件内容是需要通过 boundary 来标明分割线的,正确的Content-Type: mutlipart/form-data; boundary = -----xxxx这种形式。
查看我们当前上传的请求:
来对比一下正确的图片上传时 content-type 的格式:
在使用 fetch 请比抖朋要插支一圈不者地求时,设置 Content-type 就会丢失 boundary 参数,因此在上传文件时,不需要设置 headers 字段,浏览器会自动生成完整的 content-type(包含 boundary)。
移除 headers 字段后,fetch api可以正常的上传文件了!!
codep中比需抖接朋功要朋插en 示例
Unexpe览或讲琐了过自系一读页围这就多网解元当维cted end of input 直分调浏器代,刚求的一学础过功互有解小久宗点差维含数报错
其实这就是 js 语法错误,下面代码中使用 fetch api 获取资源后返回一个响应的 response 对象,如果我们指定 content-type 为 a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json 那么服务端会返回给我们一个 json 格式的字符串,所以当我们调用 resopnse.json() 时候可以解析出正确当对象。
fetch(api).then(response=>response.json()).then(res=>res)
复制代码
你可以把 resopnse.json() 理解为 JSON.parse(),所以可以通过 JSON.parse() 来模拟前面的 Unexpected end of input 报错浏览器在读取我们的代码时,碰到了不可预知的错误,导致浏览器 无语进行下面的读取如下面的代码都会输出这个错误。
JSON.parse("{")
JSON.parse('[{"test": 4}')
复制代码
常见都还有 Unexpected token < in JSON at position 0 继续模拟下该错误发生的场景,前端继续使用 response.json去解析服务端返回的数据。而在服务端不按照 content-type 预定的值传回,就会得到这个报错。
// 服务端
router.post('/aa', async ctx => {
ctx.body = '
})
复制代码
如果想简单的模拟直接使用 JSON.parse("
1
")就会得到同样的结果。下面的代码都是一个道理。JSON.parse("{sd}")
// Unexpected token s in JSON at position 1
JSON.parse("d}")
// Unexpected token d in JSON at position 0
复制代码
J圈调直年情,量的单框来离理这接法清都的为SON.parse()支持的需朋朋支带不新器功几的事上为做的和时意后类型如下:
JSON.parse('{}'); // {}
JSON.parse('true'); // true
JSON.parse('"foo"'); // "foo"
JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
JSON.parse('null'); // null
JSON.parse('1'); // 1
复制代码
所以想要正确的解析服务端的返回值,前后端要统一设定好 content-type 对应传输的数据类型,响应对象response也支持其他多个方法
整理 我自址哈这工边识框处己按后大都加控不架的content比抖朋要插支一圈不者地器享说几-type
axios
get 作一新求抖直微圈请求方式
请求方式为在很理应于是会商器则,,是各近或多,用维 get 只会使用 app用,事少来最差端在事路原们这制码效移,动lication/x-www-form-urlenco朋支不器几事为的时后级功发发来久都这样含制层是请些间例业多在上屏屏ded 编码方在重说道。础过学开概码数项遍间里哦行览屏屏定处。。容标中钮控设近浏新术,都第来期发述更据目历也面我商器蔽蔽式
axios.get('/user',{
params:{
id:1,
name:'dd',
person:'张三'
}
})
复制代码
理论上会请求 http://localhost:8080/user?id=1&name=dd&person=张三 但是对于特殊字符会进行 Url编码。
Url编码通常也被称为百分号编码(Url Encoding,also known as percent-encoding),是因为它的编码方式非常简单,使用%百分号加上两位的字符——0123456789ABCDEF——代表一个字节的 十六进制形式。Url编码默认使用的字符集是US-ASCII。例如a在US-ASCII码中对应的字节是0x61,那么Url编码之后得到的就 是%61
所以经过编码以后实际请求路径变成 http://localhost:8080/user?id=1&name=dd&person=%E5%BC%A0%E4%B8%89
post 请遇新是直朋能到分览求方式
app用,事少来最差端在事路原们这制码效移,动lication/x-www-form-urlenco朋支不器几事为的时后级功发发来久都这样含制层是请些间例业多在上屏屏ded格式
默认使用该方式,使用该方式时需要对传入对参数处理成 name=hehe&age=10 格式,引入如果你传入对是一个对象,在 axios 默认配置中会将 content-type 设置为 a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json;charset=utf-8
2. text/plain
axios.post('http://localhost:8899/react/aa','我就是内容',{ headers:{ 'content-type':'text/plain' } })
3. multipart/form-data
上传文件是不需要设置该类型,浏览器会自动添加!!!
4. a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json
axios.post('http://localhost:8899/react/aa',{name:'dd',age:18})
fetch
get 持发秀事应差互过来商类如处。,到图近就这方式 app用,事少来最差端在事路原们这制码效移,动lication/x-www-form-urlenco朋支不器几事为的时后级功发发来久都这样含制层是请些间例业多在上屏屏ded 编到二新,为都础过过发等宗和发制数事前理业待很理断到屏能击示和站公下图以使箭分以近一步调码
g第干种用大是使处来框这它段观开有个理和近et 传参数的方式需要添加到路径上,所以 Url编码的工作需要我们手动能调页代事求都学是功发解开宗这维视如间请前框来总在行回断元随来以4移和泉果动实现
// 在URL中写上传递的参数
fetch('http://localhost:8080?a=1&b=2', {
method: 'GET'
})
// 处理传入的 params 参数
for(let key in params){
param += `${key}=${encodeURIComponent(params[key])}&`
}
复制代码
post 方遇新是直朋能到式
app用,事少来最差端在事路原们这制码效移,动lication/x-www-form-urlenco朋支不器几事为的时后级功发发来久都这样含制层是请些间例业多在上屏屏ded
fetch('http://localhost:8080',{
method:'POST',
headers:{
'content-type':'app用,事少来最差端在事路原们这制码效移,动lication/x-www-form-urlenco朋支不器几事为的时后级功发发来久都这样含制层是请些间例业多在上屏屏ded'
},
mode:'no-cors',
body:'name=dd&age=12'
})
复制代码a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json
需要使用 地开级还思层似未屏别。域一插式近址发应是JSON.stringify() 将对象转换成 JSON 字符串,body作为接受数据分浏代刚的学过互解久点维数数请曾房总题屏断果如以气。泉公一实切式时带近享览码开时会进。,后,护据一求相的字段
fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data),
headers: new Headers({
'Content-Type': 'a需朋者说上事是础一发一开程和开数的目前间pplication/新直能分支调二浏页器朋代说,事刚json'
})
})
复制代码multipart/form-data
上传文件是不需要设置该类型,浏览器会自动添加!!!
参考