diff --git a/packages/-ember-data/tests/unit/adapters/json-api-adapter/ajax-options-test.js b/packages/-ember-data/tests/unit/adapters/json-api-adapter/ajax-options-test.js index febd895919b..8b010f2ec67 100644 --- a/packages/-ember-data/tests/unit/adapters/json-api-adapter/ajax-options-test.js +++ b/packages/-ember-data/tests/unit/adapters/json-api-adapter/ajax-options-test.js @@ -7,7 +7,7 @@ import DS from 'ember-data'; let Person, Place, store, adapter, env; -module('unit/adapters/json-api-adapter/ajax-options - building requests', function(hooks) { +module('unit/adapters/json-api-adapter/ajax-options - building requests with fetch', function(hooks) { hooks.beforeEach(function() { Person = { modelName: 'person' }; Place = { modelName: 'place' }; @@ -89,4 +89,58 @@ module('unit/adapters/json-api-adapter/ajax-options - building requests', functi 'headers assigned, Accept header not overwritten' ); }); + + test('ajaxOptions() headers are set POST', function(assert) { + adapter.headers = {}; + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, { data: { type: 'post' } }); + let receivedHeaders = ajaxOptions.headers; + + assert.deepEqual( + receivedHeaders, + { + Accept: 'application/vnd.api+json', + 'content-type': 'application/vnd.api+json', + }, + 'headers assigned on POST' + ); + }); + + test('ajaxOptions() does not override with existing headers["Content-Type"] POST', function(assert) { + adapter.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, { data: { type: 'post' } }); + let receivedHeaders = ajaxOptions.headers; + + assert.deepEqual( + receivedHeaders, + { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: 'application/vnd.api+json', + }, + 'content-type header not overwritten' + ); + }); + + test('ajaxOptions() can override with options.contentType POST', function(assert) { + adapter.headers = {}; + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, { + contentType: 'application/x-www-form-urlencoded', + data: { type: 'post' }, + }); + let receivedHeaders = ajaxOptions.headers; + + assert.deepEqual( + receivedHeaders, + { + 'content-type': 'application/x-www-form-urlencoded', + Accept: 'application/vnd.api+json', + }, + 'content-type header overwritten' + ); + }); }); diff --git a/packages/-ember-data/tests/unit/adapters/rest-adapter/ajax-options-test.js b/packages/-ember-data/tests/unit/adapters/rest-adapter/ajax-options-test.js index 9ebd157c783..86fae73662d 100644 --- a/packages/-ember-data/tests/unit/adapters/rest-adapter/ajax-options-test.js +++ b/packages/-ember-data/tests/unit/adapters/rest-adapter/ajax-options-test.js @@ -8,7 +8,7 @@ import DS from 'ember-data'; var Person, Place, store, adapter, env; -module('unit/adapters/rest-adapter/ajax-options - building requests', function(hooks) { +module('unit/adapters/rest-adapter/ajax-options - building requests with fetch', function(hooks) { hooks.beforeEach(function() { Person = { modelName: 'person' }; Place = { modelName: 'place' }; @@ -82,7 +82,6 @@ module('unit/adapters/rest-adapter/ajax-options - building requests', function(h let url = 'example.com'; let type = 'GET'; let ajaxOptions = adapter.ajaxOptions(url, type, { data: { key: 'value' } }); - delete ajaxOptions.beforeSend; assert.deepEqual(ajaxOptions, { credentials: 'same-origin', @@ -100,7 +99,6 @@ module('unit/adapters/rest-adapter/ajax-options - building requests', function(h let url = 'example.com'; let type = 'POST'; let ajaxOptions = adapter.ajaxOptions(url, type, { data: { key: 'value' } }); - delete ajaxOptions.beforeSend; assert.deepEqual(ajaxOptions, { credentials: 'same-origin', @@ -115,11 +113,55 @@ module('unit/adapters/rest-adapter/ajax-options - building requests', function(h }); }); + test('ajaxOptions() can provide own headers["Content-Type"]', function(assert) { + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: { key: 'value' }, + }); + + assert.deepEqual(ajaxOptions, { + credentials: 'same-origin', + data: { key: 'value' }, + body: '{"key":"value"}', + type: 'POST', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + url: 'example.com', + }); + }); + + test('ajaxOptions() can provide own contentType in options', function(assert) { + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, { + contentType: 'application/x-www-form-urlencoded', + data: { key: 'value' }, + }); + + assert.deepEqual(ajaxOptions, { + contentType: 'application/x-www-form-urlencoded', + credentials: 'same-origin', + data: { key: 'value' }, + body: '{"key":"value"}', + type: 'POST', + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + url: 'example.com', + }); + }); + test('ajaxOptions() empty data', function(assert) { let url = 'example.com'; let type = 'POST'; let ajaxOptions = adapter.ajaxOptions(url, type, {}); - delete ajaxOptions.beforeSend; assert.deepEqual(ajaxOptions, { credentials: 'same-origin', @@ -145,4 +187,47 @@ module('unit/adapters/rest-adapter/ajax-options - building requests', function(h return fetchPlacePromise; }); }); + + module('ajax-options - ajax', function(hooks) { + hooks.beforeEach(function() { + adapter.set('useFetch', false); + }); + + hooks.afterEach(function() { + run(() => { + store.destroy(); + env.container.destroy(); + }); + }); + + test('ajaxOptions() Content-Type is not set with ajax GET', function(assert) { + adapter.headers = {}; + + let url = 'example.com'; + let type = 'GET'; + let ajaxOptions = adapter.ajaxOptions(url, type, {}); + + assert.notOk(ajaxOptions.contentType, 'contentType not set with GET'); + }); + + test('ajaxOptions() Content-Type is not set with ajax POST no data', function(assert) { + adapter.headers = {}; + + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, {}); + + assert.notOk(ajaxOptions.contentType, 'contentType not set with POST no data'); + }); + + test('ajaxOptions() Content-Type is set with ajax POST with data', function(assert) { + adapter.headers = {}; + + let url = 'example.com'; + let type = 'POST'; + let ajaxOptions = adapter.ajaxOptions(url, type, { data: { type: 'post' } }); + + assert.equal(ajaxOptions.contentType, 'application/json; charset=utf-8', 'contentType is set with POST'); + }); + }); }); diff --git a/packages/adapter/addon/json-api.js b/packages/adapter/addon/json-api.js index e473453a413..d86da31278a 100644 --- a/packages/adapter/addon/json-api.js +++ b/packages/adapter/addon/json-api.js @@ -143,6 +143,8 @@ import { pluralize } from 'ember-inflector'; const JSONAPIAdapter = RESTAdapter.extend({ defaultSerializer: '-json-api', + _defaultContentType: 'application/vnd.api+json', + /** @method ajaxOptions @private @@ -152,9 +154,8 @@ const JSONAPIAdapter = RESTAdapter.extend({ @return {Object} */ ajaxOptions(url, type, options = {}) { - options.contentType = options.contentType || 'application/vnd.api+json'; - let hash = this._super(url, type, options); + hash.headers['Accept'] = hash.headers['Accept'] || 'application/vnd.api+json'; return hash; diff --git a/packages/adapter/addon/rest.js b/packages/adapter/addon/rest.js index 763f56a8234..16d64b6faa7 100644 --- a/packages/adapter/addon/rest.js +++ b/packages/adapter/addon/rest.js @@ -291,6 +291,8 @@ const hasNajax = typeof najax !== 'undefined'; const RESTAdapter = Adapter.extend(BuildURLMixin, { defaultSerializer: '-rest', + _defaultContentType: 'application/json; charset=utf-8', + fastboot: computed(function() { return getOwner(this).lookup('service:fastboot'); }), @@ -1093,14 +1095,21 @@ const RESTAdapter = Adapter.extend(BuildURLMixin, { options.headers = {}; } - if (options.data && options.type !== 'GET') { - let contentType = options.contentType || 'application/json; charset=utf-8'; - options.headers['content-type'] = contentType; - } + let contentType = options.contentType || this._defaultContentType; if (get(this, 'useFetch')) { + if (options.data && options.type !== 'GET') { + if (!options.headers['Content-Type'] && !options.headers['content-type']) { + options.headers['content-type'] = contentType; + } + } options = fetchOptions(options, this); } else { + // GET requests without a body should not have a content-type header + // and may be unexpected by a server + if (options.data && options.type !== 'GET') { + options = assign(options, { contentType }); + } options = ajaxOptions(options, this); } @@ -1370,7 +1379,6 @@ function ajaxOptions(options, adapter) { if (options.data && options.type !== 'GET') { options.data = JSON.stringify(options.data); - options.contentType = 'application/json; charset=utf-8'; } options.beforeSend = function(xhr) {