Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: remoting/webapp/crd/js/xhr.js

Issue 945033002: Updated XHR API so call sites are more descriptive. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@xhr-test
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 6 * @fileoverview
7 * Simple utilities for making XHRs more pleasant. 7 * Simple utilities for making XHRs more pleasant.
8 */ 8 */
9 9
10 'use strict'; 10 'use strict';
11 11
12 /** @suppress {duplicate} */ 12 /** @suppress {duplicate} */
13 var remoting = remoting || {}; 13 var remoting = remoting || {};
14 14
15 /** Namespace for XHR functions */ 15 /** Namespace for XHR functions */
16 /** @type {Object} */ 16 /** @type {Object} */
17 remoting.xhr = remoting.xhr || {}; 17 remoting.xhr = remoting.xhr || {};
18 18
19 /** 19 /**
20 * Takes an associative array of parameters and urlencodes it. 20 * Takes an associative array of parameters and urlencodes it.
21 * 21 *
22 * @param {Object.<string>} paramHash The parameter key/value pairs. 22 * @param {Object.<string,string>} paramHash The parameter key/value pairs.
23 * @return {string} URLEncoded version of paramHash. 23 * @return {string} URLEncoded version of paramHash.
24 */ 24 */
25 remoting.xhr.urlencodeParamHash = function(paramHash) { 25 remoting.xhr.urlencodeParamHash = function(paramHash) {
26 var paramArray = []; 26 var paramArray = [];
27 for (var key in paramHash) { 27 for (var key in paramHash) {
28 paramArray.push(encodeURIComponent(key) + 28 paramArray.push(encodeURIComponent(key) +
29 '=' + encodeURIComponent(paramHash[key])); 29 '=' + encodeURIComponent(paramHash[key]));
30 } 30 }
31 if (paramArray.length > 0) { 31 if (paramArray.length > 0) {
32 paramArray.sort(); // To simplify testing. 32 paramArray.sort(); // To simplify testing.
33 return paramArray.join('&'); 33 return paramArray.join('&');
34 } 34 }
35 return ''; 35 return '';
36 }; 36 };
37 37
38 /** 38 /**
39 * Execute an XHR GET asynchronously. 39 * Parameters for the 'start' function.
Jamie 2015/02/21 01:35:48 I'm not a big fan of mixing optional and mandatory
John Williams 2015/02/23 23:00:55 I prefer to keep it the way it is, because when th
40 * 40 *
41 * @param {string} url The base URL to GET, excluding parameters. 41 * method: The HTTP method to use.
42 * @param {function(XMLHttpRequest):void} onDone The function to call on 42 *
43 * completion. 43 * url: The URL to request.
44 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 44 *
45 * either as an associative array, or a string. If it is a string, do 45 * onDone: Function to call when the XHR finishes.
46 * not include the ? and be sure it is correctly URLEncoded. 46
47 * @param {Object.<string>=} opt_headers Additional headers to include on the 47 * urlParams: (optional) Parameters to be appended to the URL.
48 * request. 48 * Null-valued parameters are omitted.
49 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the 49 *
50 * XHR. 50 * textContent: (optional) Text to be sent as the request body.
51 * @return {XMLHttpRequest} The request object. 51 *
52 * fromContent: (optional) Data to be URL-encoded and sent as the
Jamie 2015/02/21 01:35:48 s/from/form/
John Williams 2015/02/23 23:00:55 Done.
53 * request body. Causes Content-type header to be set
54 * appropriately.
55 *
56 * jsonContent: (optional) Data to be JSON-encoded and sent as the
57 * request body. Causes Content-type header to be set
58 * appropriately.
59 *
60 * headers: (optional) Additional request headers to be sent.
61 * Null-valued headers are omitted.
62 *
63 * withCredentials: (optional) Value of the XHR's withCredentials field.
64 *
65 * oauthToken: (optional) An OAuth2 token used to construct an
66 * Authentication header.
67 *
68 * @typedef {{
69 * method: string,
70 * url:string,
71 * onDone:(function(XMLHttpRequest):void),
72 * urlParams:(string|Object<string,?string>|undefined),
73 * textContent:(string|undefined),
74 * formContent:(Object|undefined),
75 * jsonContent:(*|undefined),
76 * headers:(Object<string,?string>|undefined),
77 * withCredentials:(boolean|undefined),
78 * oauthToken:(string|undefined)
79 * }}
52 */ 80 */
53 remoting.xhr.get = function(url, onDone, opt_parameters, opt_headers, 81 remoting.XhrParams;
54 opt_withCredentials) { 82
55 return remoting.xhr.doMethod('GET', url, onDone, opt_parameters, 83 /**
56 opt_headers, opt_withCredentials); 84 * Returns a copy of the input object with all null or undefined
85 * fields removed.
86 *
87 * @param {Object<string,?string>|undefined} input
88 * @return {!Object<string,string>}
89 * @private
90 */
91 remoting.xhr.removeNullFields_ = function(input) {
92 /** @type {!Object<string,string>} */
93 var result = {};
94 if (input) {
95 for (var field in input) {
96 var value = input[field];
97 if (value != null) {
98 result[field] = value;
99 }
100 }
101 }
102 return result;
57 }; 103 };
58 104
59 /** 105 /**
60 * Execute an XHR POST asynchronously. 106 * Executes an arbitrary HTTP method asynchronously.
61 * 107 *
62 * @param {string} url The base URL to POST, excluding parameters. 108 * @param {remoting.XhrParams} params
63 * @param {function(XMLHttpRequest):void} onDone The function to call on 109 * @return {XMLHttpRequest} The XMLHttpRequest object.
64 * completion.
65 * @param {(string|Object.<string>)=} opt_parameters The request parameters,
66 * either as an associative array, or a string. If it is a string, be
67 * sure it is correctly URLEncoded.
68 * @param {Object.<string>=} opt_headers Additional headers to include on the
69 * request.
70 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the
71 * XHR.
72 * @return {XMLHttpRequest} The request object.
73 */ 110 */
74 remoting.xhr.post = function(url, onDone, opt_parameters, opt_headers, 111 remoting.xhr.start = function(params) {
75 opt_withCredentials) { 112 // Extract fields that can be used more or less as-is.
76 return remoting.xhr.doMethod('POST', url, onDone, opt_parameters, 113 var method = params.method;
77 opt_headers, opt_withCredentials); 114 var url = params.url;
115 var onDone = params.onDone;
116 var headers = remoting.xhr.removeNullFields_(params.headers);
117 var withCredentials = params.withCredentials || false;
118
119 // Apply URL parameters.
120 var parameterString = '';
121 if (typeof(params.urlParams) === 'string') {
122 parameterString = params.urlParams;
123 } else if (typeof(params.urlParams) === 'object') {
124 parameterString = remoting.xhr.urlencodeParamHash(
125 remoting.xhr.removeNullFields_(params.urlParams));
126 }
127 if (parameterString) {
128 base.debug.assert(url.indexOf('?') == -1);
129 url += '?' + parameterString;
130 }
131
132 // Check that the content spec is consistent.
133 if ((0 +
Jamie 2015/02/21 01:35:48 An explicit cast using Number would be more readab
John Williams 2015/02/23 23:00:55 Changed to an explicit cast. I don't want to use s
134 (params.textContent !== undefined) +
135 (params.formContent !== undefined) +
136 (params.jsonContent !== undefined)) > 1) {
137 throw Error(
Jamie 2015/02/21 01:35:48 s/Error/new Error/ for consistency with (most of)
John Williams 2015/02/23 23:00:55 Done.
138 'may only specify one of textContent, formContent, and jsonContent');
139 }
140
141 // Convert the content fields to a single text content variable.
142 /** @type {?string} */
143 var content = null;
144 if (params.textContent !== undefined) {
145 content = params.textContent;
146 } else if (params.formContent !== undefined) {
147 if (!('Content-type' in headers)) {
148 headers['Content-type'] = 'application/x-www-form-urlencoded';
149 }
150 content = remoting.xhr.urlencodeParamHash(params.formContent);
151 } else if (params.jsonContent !== undefined) {
152 if (!('Content-type' in headers)) {
153 headers['Content-type'] = 'application/json; charset=UTF-8';
154 }
155 content = JSON.stringify(params.jsonContent);
156 }
157
158 // Apply the oauthToken field.
159 if (params.oauthToken !== undefined) {
160 base.debug.assert(!('Authorization' in headers));
161 headers['Authorization'] = 'Bearer ' + params.oauthToken;
162 }
163
164 return remoting.xhr.startInternal_(
165 method, url, onDone, content, headers, withCredentials);
78 }; 166 };
79 167
80 /** 168 /**
81 * Execute an XHR DELETE asynchronously. 169 * Executes an arbitrary HTTP method asynchronously.
82 * 170 *
83 * @param {string} url The base URL to DELETE, excluding parameters. 171 * @param {string} method
84 * @param {function(XMLHttpRequest):void} onDone The function to call on 172 * @param {string} url
85 * completion. 173 * @param {function(XMLHttpRequest):void} onDone
86 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 174 * @param {?string} content
87 * either as an associative array, or a string. If it is a string, be 175 * @param {!Object<string,string>} headers
88 * sure it is correctly URLEncoded. 176 * @param {boolean} withCredentials
89 * @param {Object.<string>=} opt_headers Additional headers to include on the 177 * @return {XMLHttpRequest} The XMLHttpRequest object.
90 * request. 178 * @private
91 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the
92 * XHR.
93 * @return {XMLHttpRequest} The request object.
94 */ 179 */
95 remoting.xhr.remove = function(url, onDone, opt_parameters, opt_headers, 180 remoting.xhr.startInternal_ = function(
96 opt_withCredentials) { 181 method, url, onDone, content, headers, withCredentials) {
97 return remoting.xhr.doMethod('DELETE', url, onDone, opt_parameters,
98 opt_headers, opt_withCredentials);
99 };
100
101 /**
102 * Execute an XHR PUT asynchronously.
103 *
104 * @param {string} url The base URL to PUT, excluding parameters.
105 * @param {function(XMLHttpRequest):void} onDone The function to call on
106 * completion.
107 * @param {(string|Object.<string>)=} opt_parameters The request parameters,
108 * either as an associative array, or a string. If it is a string, be
109 * sure it is correctly URLEncoded.
110 * @param {Object.<string>=} opt_headers Additional headers to include on the
111 * request.
112 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the
113 * XHR.
114 * @return {XMLHttpRequest} The request object.
115 */
116 remoting.xhr.put = function(url, onDone, opt_parameters, opt_headers,
117 opt_withCredentials) {
118 return remoting.xhr.doMethod('PUT', url, onDone, opt_parameters,
119 opt_headers, opt_withCredentials);
120 };
121
122 /**
123 * Execute an arbitrary HTTP method asynchronously.
124 *
125 * @param {string} methodName The HTTP method name, e.g. "GET", "POST" etc.
126 * @param {string} url The base URL, excluding parameters.
127 * @param {function(XMLHttpRequest):void} onDone The function to call on
128 * completion.
129 * @param {(string|Object.<string>)=} opt_parameters The request parameters,
130 * either as an associative array, or a string. If it is a string, be
131 * sure it is correctly URLEncoded.
132 * @param {Object.<string>=} opt_headers Additional headers to include on the
133 * request.
134 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the
135 * XHR.
136 * @return {XMLHttpRequest} The XMLHttpRequest object.
137 */
138 remoting.xhr.doMethod = function(methodName, url, onDone,
139 opt_parameters, opt_headers,
140 opt_withCredentials) {
141 /** @type {XMLHttpRequest} */ 182 /** @type {XMLHttpRequest} */
142 var xhr = new XMLHttpRequest(); 183 var xhr = new XMLHttpRequest();
143 xhr.onreadystatechange = function() { 184 xhr.onreadystatechange = function() {
144 if (xhr.readyState != 4) { 185 if (xhr.readyState != 4) {
145 return; 186 return;
146 } 187 }
147 onDone(xhr); 188 onDone(xhr);
148 }; 189 };
149 190
150 var parameterString = ''; 191 xhr.open(method, url, true);
151 if (typeof(opt_parameters) === 'string') { 192 for (var key in headers) {
152 parameterString = opt_parameters; 193 xhr.setRequestHeader(key, headers[key]);
153 } else if (typeof(opt_parameters) === 'object') {
154 parameterString = remoting.xhr.urlencodeParamHash(opt_parameters);
155 } else if (opt_parameters === undefined) {
156 // No problem here. Do nothing.
157 } else {
158 throw 'opt_parameters must be string or associated array.';
159 } 194 }
160 195 xhr.withCredentials = withCredentials;
161 var useBody = (methodName == 'POST') || (methodName == 'PUT'); 196 xhr.send(content);
162
163 if (!useBody && parameterString != '') {
164 url = url + '?' + parameterString;
165 }
166
167 xhr.open(methodName, url, true);
168 if (methodName == 'POST' &&
169 (typeof opt_headers !== 'object' ||
170 typeof opt_headers['Content-type'] !== 'string')) {
171 xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
172 }
173 // Add in request headers.
174 if (typeof(opt_headers) === 'object') {
175 for (var key in opt_headers) {
176 xhr.setRequestHeader(key, opt_headers[key]);
177 }
178 } else if (opt_headers === undefined) {
179 // No problem here. Do nothing.
180 } else {
181 throw 'opt_headers must be associative array.';
182 }
183
184 if (opt_withCredentials) {
185 xhr.withCredentials = true;
186 }
187
188 xhr.send(useBody ? parameterString : null);
189 return xhr; 197 return xhr;
190 }; 198 };
191 199
192 /** 200 /**
193 * Generic success/failure response proxy. 201 * Generic success/failure response proxy.
194 * 202 *
195 * @param {function():void} onDone 203 * @param {function():void} onDone
196 * @param {function(remoting.Error):void} onError 204 * @param {function(remoting.Error):void} onError
197 * @return {function(XMLHttpRequest):void} 205 * @return {function(XMLHttpRequest):void}
198 */ 206 */
199 remoting.xhr.defaultResponse = function(onDone, onError) { 207 remoting.xhr.defaultResponse = function(onDone, onError) {
200 /** @param {XMLHttpRequest} xhr */ 208 /** @param {XMLHttpRequest} xhr */
201 var result = function(xhr) { 209 var result = function(xhr) {
202 /** @type {remoting.Error} */ 210 /** @type {remoting.Error} */
203 var error = 211 var error =
204 remoting.Error.fromHttpStatus(/** @type {number} */ (xhr.status)); 212 remoting.Error.fromHttpStatus(/** @type {number} */ (xhr.status));
205 if (error == remoting.Error.NONE) { 213 if (error == remoting.Error.NONE) {
206 onDone(); 214 onDone();
207 } else { 215 } else {
208 onError(error); 216 onError(error);
209 } 217 }
210 }; 218 };
211 return result; 219 return result;
212 }; 220 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698