提交 1e4a6151 编写于 作者: E Emily Eisenberg

Make fetch add a `Content-Type` header based on the type of the body.

Summary: The process of extracting a body from a passed in body object
(described here: https://fetch.spec.whatwg.org/#concept-bodyinit-extract)
creates both a byte stream and a corresponding `Content-Type` value. These two
values are used in the constructor of `Request` and `Response` objects to
initialize the body and to set an initial `Content-Type` header.

This change makes `Body._initBody()` return the `Content-Type` value
corresponding to the type of the object passed in, and to set that header in
the corresponding `Request` and `Response` objects that are being constructed.

There's a little bit of a dance I need to do in Request to keep track of where
the body is coming from, because we're only supposed to be setting the
`Content-Type` header if we're getting the body from the options passed in, not
if we're copying it from another `Request`. See step 32 of
https://fetch.spec.whatwg.org/#dom-request. (This was caught by one of the
tests. Yay tests!)

I added some corresponding tests to make sure this works.

Test Plan:
I'm a little bit confused about how to run the tests normally, but this is what
I did:

 - Run `make lint`, see that everything passes
 - Start up the test server by running `node script/server 3900 &`
 - Visit http://localhost:3900/test/test.html and
   http://localhost:3900/test/test-worker.html in Chrome and see that all of
   the tests pass.

From what I can tell, running the tests in Chrome just used the native fetch
objects, so to test the polyfill I added `window.fetch = null` in
test/test.html before it includes '../fetch.js', and added `self.fetch = null`
in test/worker.js before it imports '../fetch.js'. Then:

 - Visit http://localhost:3900/test/test.html and
   http://localhost:3900/test/test-worker.html in Chrome and see that all of
   the tests pass.

Reviewers: @jeresig
上级 133bd732
...@@ -128,10 +128,16 @@ ...@@ -128,10 +128,16 @@
this._bodyInit = body this._bodyInit = body
if (typeof body === 'string') { if (typeof body === 'string') {
this._bodyText = body this._bodyText = body
return 'text/plain;charset=UTF-8'
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) { } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
this._bodyBlob = body this._bodyBlob = body
if (body.type !== '') {
return body.type
}
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) { } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
this._bodyFormData = body this._bodyFormData = body
// Since we don't know what the multipart/form-data boundary string
// will be, we can't set a Content-Type here
} else if (!body) { } else if (!body) {
this._bodyText = '' this._bodyText = ''
} else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) { } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) {
...@@ -207,6 +213,7 @@ ...@@ -207,6 +213,7 @@
function Request(input, options) { function Request(input, options) {
options = options || {} options = options || {}
var body = options.body var body = options.body
var bodyFromOptions = true
if (Request.prototype.isPrototypeOf(input)) { if (Request.prototype.isPrototypeOf(input)) {
if (input.bodyUsed) { if (input.bodyUsed) {
throw new TypeError('Already read') throw new TypeError('Already read')
...@@ -221,6 +228,7 @@ ...@@ -221,6 +228,7 @@
if (!body) { if (!body) {
body = input._bodyInit body = input._bodyInit
input.bodyUsed = true input.bodyUsed = true
bodyFromOptions = false
} }
} else { } else {
this.url = input this.url = input
...@@ -237,7 +245,10 @@ ...@@ -237,7 +245,10 @@
if ((this.method === 'GET' || this.method === 'HEAD') && body) { if ((this.method === 'GET' || this.method === 'HEAD') && body) {
throw new TypeError('Body not allowed for GET or HEAD requests') throw new TypeError('Body not allowed for GET or HEAD requests')
} }
this._initBody(body) var contentType = this._initBody(body)
if (bodyFromOptions && contentType && !this.headers.get('Content-Type')) {
this.headers.set('Content-Type', contentType)
}
} }
Request.prototype.clone = function() { Request.prototype.clone = function() {
...@@ -276,12 +287,15 @@ ...@@ -276,12 +287,15 @@
options = {} options = {}
} }
this._initBody(bodyInit) this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
var contentType = this._initBody(bodyInit)
if (contentType && !this.headers.get('Content-Type')) {
this.headers.set('Content-Type', contentType)
}
this.type = 'default' this.type = 'default'
this.status = options.status this.status = options.status
this.ok = this.status >= 200 && this.status < 300 this.ok = this.status >= 200 && this.status < 300
this.statusText = options.statusText this.statusText = options.statusText
this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
this.url = options.url || '' this.url = options.url || ''
} }
......
...@@ -307,6 +307,36 @@ suite('Request', function() { ...@@ -307,6 +307,36 @@ suite('Request', function() {
}) })
}) })
test('construct with string body sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8')
})
test('construct with Blob body and type sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: new Blob(['test'], { type: 'text/plain' }),
})
assert.equal(req.headers.get('content-type'), 'text/plain')
})
test('construct with body and explicit header uses header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: {
'Content-Type': 'text/plain'
},
body: 'I work out'
})
assert.equal(req.headers.get('content-type'), 'text/plain')
})
test('clone request', function() { test('clone request', function() {
var req = new Request('https://fetch.spec.whatwg.org/', { var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post', method: 'post',
...@@ -479,6 +509,26 @@ suite('Response', function() { ...@@ -479,6 +509,26 @@ suite('Response', function() {
assert.equal(r.status, 301) assert.equal(r.status, 301)
assert.equal(r.headers.get('Location'), 'https://fetch.spec.whatwg.org/') assert.equal(r.headers.get('Location'), 'https://fetch.spec.whatwg.org/')
}) })
test('construct with string body sets Content-Type header', function() {
var r = new Response('I work out')
assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8')
})
test('construct with Blob body and type sets Content-Type header', function() {
var r = new Response(new Blob(['test'], { type: 'text/plain' }))
assert.equal(r.headers.get('content-type'), 'text/plain')
})
test('construct with body and explicit header uses header', function() {
var r = new Response('I work out', {
headers: {
'Content-Type': 'text/plain'
},
})
assert.equal(r.headers.get('content-type'), 'text/plain')
})
}) })
// https://fetch.spec.whatwg.org/#body-mixin // https://fetch.spec.whatwg.org/#body-mixin
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册