HTTP.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Protocol.js
  7. * @requires OpenLayers/Request/XMLHttpRequest.js
  8. */
  9. /**
  10. * if application uses the query string, for example, for BBOX parameters,
  11. * OpenLayers/Format/QueryStringFilter.js should be included in the build config file
  12. */
  13. /**
  14. * Class: OpenLayers.Protocol.HTTP
  15. * A basic HTTP protocol for vector layers. Create a new instance with the
  16. * <OpenLayers.Protocol.HTTP> constructor.
  17. *
  18. * Inherits from:
  19. * - <OpenLayers.Protocol>
  20. */
  21. OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
  22. /**
  23. * Property: url
  24. * {String} Service URL, read-only, set through the options
  25. * passed to constructor.
  26. */
  27. url: null,
  28. /**
  29. * Property: headers
  30. * {Object} HTTP request headers, read-only, set through the options
  31. * passed to the constructor,
  32. * Example: {'Content-Type': 'plain/text'}
  33. */
  34. headers: null,
  35. /**
  36. * Property: params
  37. * {Object} Parameters of GET requests, read-only, set through the options
  38. * passed to the constructor,
  39. * Example: {'bbox': '5,5,5,5'}
  40. */
  41. params: null,
  42. /**
  43. * Property: callback
  44. * {Object} Function to be called when the <read>, <create>,
  45. * <update>, <delete> or <commit> operation completes, read-only,
  46. * set through the options passed to the constructor.
  47. */
  48. callback: null,
  49. /**
  50. * Property: scope
  51. * {Object} Callback execution scope, read-only, set through the
  52. * options passed to the constructor.
  53. */
  54. scope: null,
  55. /**
  56. * APIProperty: readWithPOST
  57. * {Boolean} true if read operations are done with POST requests
  58. * instead of GET, defaults to false.
  59. */
  60. readWithPOST: false,
  61. /**
  62. * APIProperty: updateWithPOST
  63. * {Boolean} true if update operations are done with POST requests
  64. * defaults to false.
  65. */
  66. updateWithPOST: false,
  67. /**
  68. * APIProperty: deleteWithPOST
  69. * {Boolean} true if delete operations are done with POST requests
  70. * defaults to false.
  71. * if true, POST data is set to output of format.write().
  72. */
  73. deleteWithPOST: false,
  74. /**
  75. * Property: wildcarded.
  76. * {Boolean} If true percent signs are added around values
  77. * read from LIKE filters, for example if the protocol
  78. * read method is passed a LIKE filter whose property
  79. * is "foo" and whose value is "bar" the string
  80. * "foo__ilike=%bar%" will be sent in the query string;
  81. * defaults to false.
  82. */
  83. wildcarded: false,
  84. /**
  85. * APIProperty: srsInBBOX
  86. * {Boolean} Include the SRS identifier in BBOX query string parameter.
  87. * Default is false. If true and the layer has a projection object set,
  88. * any BBOX filter will be serialized with a fifth item identifying the
  89. * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
  90. */
  91. srsInBBOX: false,
  92. /**
  93. * Constructor: OpenLayers.Protocol.HTTP
  94. * A class for giving layers generic HTTP protocol.
  95. *
  96. * Parameters:
  97. * options - {Object} Optional object whose properties will be set on the
  98. * instance.
  99. *
  100. * Valid options include:
  101. * url - {String}
  102. * headers - {Object}
  103. * params - {Object} URL parameters for GET requests
  104. * format - {<OpenLayers.Format>}
  105. * callback - {Function}
  106. * scope - {Object}
  107. */
  108. initialize: function(options) {
  109. options = options || {};
  110. this.params = {};
  111. this.headers = {};
  112. OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
  113. if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
  114. var format = new OpenLayers.Format.QueryStringFilter({
  115. wildcarded: this.wildcarded,
  116. srsInBBOX: this.srsInBBOX
  117. });
  118. this.filterToParams = function(filter, params) {
  119. return format.write(filter, params);
  120. };
  121. }
  122. },
  123. /**
  124. * APIMethod: destroy
  125. * Clean up the protocol.
  126. */
  127. destroy: function() {
  128. this.params = null;
  129. this.headers = null;
  130. OpenLayers.Protocol.prototype.destroy.apply(this);
  131. },
  132. /**
  133. * APIMethod: filterToParams
  134. * Optional method to translate an <OpenLayers.Filter> object into an object
  135. * that can be serialized as request query string provided. If a custom
  136. * method is not provided, the filter will be serialized using the
  137. * <OpenLayers.Format.QueryStringFilter> class.
  138. *
  139. * Parameters:
  140. * filter - {<OpenLayers.Filter>} filter to convert.
  141. * params - {Object} The parameters object.
  142. *
  143. * Returns:
  144. * {Object} The resulting parameters object.
  145. */
  146. /**
  147. * APIMethod: read
  148. * Construct a request for reading new features.
  149. *
  150. * Parameters:
  151. * options - {Object} Optional object for configuring the request.
  152. * This object is modified and should not be reused.
  153. *
  154. * Valid options:
  155. * url - {String} Url for the request.
  156. * params - {Object} Parameters to get serialized as a query string.
  157. * headers - {Object} Headers to be set on the request.
  158. * filter - {<OpenLayers.Filter>} Filter to get serialized as a
  159. * query string.
  160. * readWithPOST - {Boolean} If the request should be done with POST.
  161. *
  162. * Returns:
  163. * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
  164. * references the HTTP request, this object is also passed to the
  165. * callback function when the request completes, its "features" property
  166. * is then populated with the features received from the server.
  167. */
  168. read: function(options) {
  169. OpenLayers.Protocol.prototype.read.apply(this, arguments);
  170. options = options || {};
  171. options.params = OpenLayers.Util.applyDefaults(
  172. options.params, this.options.params);
  173. options = OpenLayers.Util.applyDefaults(options, this.options);
  174. if (options.filter && this.filterToParams) {
  175. options.params = this.filterToParams(
  176. options.filter, options.params
  177. );
  178. }
  179. var readWithPOST = (options.readWithPOST !== undefined) ?
  180. options.readWithPOST : this.readWithPOST;
  181. var resp = new OpenLayers.Protocol.Response({requestType: "read"});
  182. if(readWithPOST) {
  183. var headers = options.headers || {};
  184. headers["Content-Type"] = "application/x-www-form-urlencoded";
  185. resp.priv = OpenLayers.Request.POST({
  186. url: options.url,
  187. callback: this.createCallback(this.handleRead, resp, options),
  188. data: OpenLayers.Util.getParameterString(options.params),
  189. headers: headers
  190. });
  191. } else {
  192. resp.priv = OpenLayers.Request.GET({
  193. url: options.url,
  194. callback: this.createCallback(this.handleRead, resp, options),
  195. params: options.params,
  196. headers: options.headers
  197. });
  198. }
  199. return resp;
  200. },
  201. /**
  202. * Method: handleRead
  203. * Individual callbacks are created for read, create and update, should
  204. * a subclass need to override each one separately.
  205. *
  206. * Parameters:
  207. * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
  208. * the user callback.
  209. * options - {Object} The user options passed to the read call.
  210. */
  211. handleRead: function(resp, options) {
  212. this.handleResponse(resp, options);
  213. },
  214. /**
  215. * APIMethod: create
  216. * Construct a request for writing newly created features.
  217. *
  218. * Parameters:
  219. * features - {Array({<OpenLayers.Feature.Vector>})} or
  220. * {<OpenLayers.Feature.Vector>}
  221. * options - {Object} Optional object for configuring the request.
  222. * This object is modified and should not be reused.
  223. *
  224. * Returns:
  225. * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
  226. * object, whose "priv" property references the HTTP request, this
  227. * object is also passed to the callback function when the request
  228. * completes, its "features" property is then populated with the
  229. * the features received from the server.
  230. */
  231. create: function(features, options) {
  232. options = OpenLayers.Util.applyDefaults(options, this.options);
  233. var resp = new OpenLayers.Protocol.Response({
  234. reqFeatures: features,
  235. requestType: "create"
  236. });
  237. resp.priv = OpenLayers.Request.POST({
  238. url: options.url,
  239. callback: this.createCallback(this.handleCreate, resp, options),
  240. headers: options.headers,
  241. data: this.format.write(features)
  242. });
  243. return resp;
  244. },
  245. /**
  246. * Method: handleCreate
  247. * Called the the request issued by <create> is complete. May be overridden
  248. * by subclasses.
  249. *
  250. * Parameters:
  251. * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
  252. * any user callback.
  253. * options - {Object} The user options passed to the create call.
  254. */
  255. handleCreate: function(resp, options) {
  256. this.handleResponse(resp, options);
  257. },
  258. /**
  259. * APIMethod: update
  260. * Construct a request updating modified feature.
  261. *
  262. * Parameters:
  263. * feature - {<OpenLayers.Feature.Vector>}
  264. * options - {Object} Optional object for configuring the request.
  265. * This object is modified and should not be reused.
  266. *
  267. * Returns:
  268. * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
  269. * object, whose "priv" property references the HTTP request, this
  270. * object is also passed to the callback function when the request
  271. * completes, its "features" property is then populated with the
  272. * the feature received from the server.
  273. */
  274. update: function(feature, options) {
  275. options = options || {};
  276. var url = options.url ||
  277. feature.url ||
  278. this.options.url + "/" + feature.fid;
  279. options = OpenLayers.Util.applyDefaults(options, this.options);
  280. var resp = new OpenLayers.Protocol.Response({
  281. reqFeatures: feature,
  282. requestType: "update"
  283. });
  284. var method = this.updateWithPOST ? "POST" : "PUT";
  285. resp.priv = OpenLayers.Request[method]({
  286. url: url,
  287. callback: this.createCallback(this.handleUpdate, resp, options),
  288. headers: options.headers,
  289. data: this.format.write(feature)
  290. });
  291. return resp;
  292. },
  293. /**
  294. * Method: handleUpdate
  295. * Called the the request issued by <update> is complete. May be overridden
  296. * by subclasses.
  297. *
  298. * Parameters:
  299. * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
  300. * any user callback.
  301. * options - {Object} The user options passed to the update call.
  302. */
  303. handleUpdate: function(resp, options) {
  304. this.handleResponse(resp, options);
  305. },
  306. /**
  307. * APIMethod: delete
  308. * Construct a request deleting a removed feature.
  309. *
  310. * Parameters:
  311. * feature - {<OpenLayers.Feature.Vector>}
  312. * options - {Object} Optional object for configuring the request.
  313. * This object is modified and should not be reused.
  314. *
  315. * Returns:
  316. * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
  317. * object, whose "priv" property references the HTTP request, this
  318. * object is also passed to the callback function when the request
  319. * completes.
  320. */
  321. "delete": function(feature, options) {
  322. options = options || {};
  323. var url = options.url ||
  324. feature.url ||
  325. this.options.url + "/" + feature.fid;
  326. options = OpenLayers.Util.applyDefaults(options, this.options);
  327. var resp = new OpenLayers.Protocol.Response({
  328. reqFeatures: feature,
  329. requestType: "delete"
  330. });
  331. var method = this.deleteWithPOST ? "POST" : "DELETE";
  332. var requestOptions = {
  333. url: url,
  334. callback: this.createCallback(this.handleDelete, resp, options),
  335. headers: options.headers
  336. };
  337. if (this.deleteWithPOST) {
  338. requestOptions.data = this.format.write(feature);
  339. }
  340. resp.priv = OpenLayers.Request[method](requestOptions);
  341. return resp;
  342. },
  343. /**
  344. * Method: handleDelete
  345. * Called the the request issued by <delete> is complete. May be overridden
  346. * by subclasses.
  347. *
  348. * Parameters:
  349. * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
  350. * any user callback.
  351. * options - {Object} The user options passed to the delete call.
  352. */
  353. handleDelete: function(resp, options) {
  354. this.handleResponse(resp, options);
  355. },
  356. /**
  357. * Method: handleResponse
  358. * Called by CRUD specific handlers.
  359. *
  360. * Parameters:
  361. * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
  362. * any user callback.
  363. * options - {Object} The user options passed to the create, read, update,
  364. * or delete call.
  365. */
  366. handleResponse: function(resp, options) {
  367. var request = resp.priv;
  368. if(options.callback) {
  369. if(request.status >= 200 && request.status < 300) {
  370. // success
  371. if(resp.requestType != "delete") {
  372. resp.features = this.parseFeatures(request);
  373. }
  374. resp.code = OpenLayers.Protocol.Response.SUCCESS;
  375. } else {
  376. // failure
  377. resp.code = OpenLayers.Protocol.Response.FAILURE;
  378. }
  379. options.callback.call(options.scope, resp);
  380. }
  381. },
  382. /**
  383. * Method: parseFeatures
  384. * Read HTTP response body and return features.
  385. *
  386. * Parameters:
  387. * request - {XMLHttpRequest} The request object
  388. *
  389. * Returns:
  390. * {Array({<OpenLayers.Feature.Vector>})} or
  391. * {<OpenLayers.Feature.Vector>} Array of features or a single feature.
  392. */
  393. parseFeatures: function(request) {
  394. var doc = request.responseXML;
  395. if (!doc || !doc.documentElement) {
  396. doc = request.responseText;
  397. }
  398. if (!doc || doc.length <= 0) {
  399. return null;
  400. }
  401. return this.format.read(doc);
  402. },
  403. /**
  404. * APIMethod: commit
  405. * Iterate over each feature and take action based on the feature state.
  406. * Possible actions are create, update and delete.
  407. *
  408. * Parameters:
  409. * features - {Array({<OpenLayers.Feature.Vector>})}
  410. * options - {Object} Optional object for setting up intermediate commit
  411. * callbacks.
  412. *
  413. * Valid options:
  414. * create - {Object} Optional object to be passed to the <create> method.
  415. * update - {Object} Optional object to be passed to the <update> method.
  416. * delete - {Object} Optional object to be passed to the <delete> method.
  417. * callback - {Function} Optional function to be called when the commit
  418. * is complete.
  419. * scope - {Object} Optional object to be set as the scope of the callback.
  420. *
  421. * Returns:
  422. * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
  423. * one per request made to the server, each object's "priv" property
  424. * references the corresponding HTTP request.
  425. */
  426. commit: function(features, options) {
  427. options = OpenLayers.Util.applyDefaults(options, this.options);
  428. var resp = [], nResponses = 0;
  429. // Divide up features before issuing any requests. This properly
  430. // counts requests in the event that any responses come in before
  431. // all requests have been issued.
  432. var types = {};
  433. types[OpenLayers.State.INSERT] = [];
  434. types[OpenLayers.State.UPDATE] = [];
  435. types[OpenLayers.State.DELETE] = [];
  436. var feature, list, requestFeatures = [];
  437. for(var i=0, len=features.length; i<len; ++i) {
  438. feature = features[i];
  439. list = types[feature.state];
  440. if(list) {
  441. list.push(feature);
  442. requestFeatures.push(feature);
  443. }
  444. }
  445. // tally up number of requests
  446. var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
  447. types[OpenLayers.State.UPDATE].length +
  448. types[OpenLayers.State.DELETE].length;
  449. // This response will be sent to the final callback after all the others
  450. // have been fired.
  451. var success = true;
  452. var finalResponse = new OpenLayers.Protocol.Response({
  453. reqFeatures: requestFeatures
  454. });
  455. function insertCallback(response) {
  456. var len = response.features ? response.features.length : 0;
  457. var fids = new Array(len);
  458. for(var i=0; i<len; ++i) {
  459. fids[i] = response.features[i].fid;
  460. }
  461. finalResponse.insertIds = fids;
  462. callback.apply(this, [response]);
  463. }
  464. function callback(response) {
  465. this.callUserCallback(response, options);
  466. success = success && response.success();
  467. nResponses++;
  468. if (nResponses >= nRequests) {
  469. if (options.callback) {
  470. finalResponse.code = success ?
  471. OpenLayers.Protocol.Response.SUCCESS :
  472. OpenLayers.Protocol.Response.FAILURE;
  473. options.callback.apply(options.scope, [finalResponse]);
  474. }
  475. }
  476. }
  477. // start issuing requests
  478. var queue = types[OpenLayers.State.INSERT];
  479. if(queue.length > 0) {
  480. resp.push(this.create(
  481. queue, OpenLayers.Util.applyDefaults(
  482. {callback: insertCallback, scope: this}, options.create
  483. )
  484. ));
  485. }
  486. queue = types[OpenLayers.State.UPDATE];
  487. for(var i=queue.length-1; i>=0; --i) {
  488. resp.push(this.update(
  489. queue[i], OpenLayers.Util.applyDefaults(
  490. {callback: callback, scope: this}, options.update
  491. ))
  492. );
  493. }
  494. queue = types[OpenLayers.State.DELETE];
  495. for(var i=queue.length-1; i>=0; --i) {
  496. resp.push(this["delete"](
  497. queue[i], OpenLayers.Util.applyDefaults(
  498. {callback: callback, scope: this}, options["delete"]
  499. ))
  500. );
  501. }
  502. return resp;
  503. },
  504. /**
  505. * APIMethod: abort
  506. * Abort an ongoing request, the response object passed to
  507. * this method must come from this HTTP protocol (as a result
  508. * of a create, read, update, delete or commit operation).
  509. *
  510. * Parameters:
  511. * response - {<OpenLayers.Protocol.Response>}
  512. */
  513. abort: function(response) {
  514. if (response) {
  515. response.priv.abort();
  516. }
  517. },
  518. /**
  519. * Method: callUserCallback
  520. * This method is used from within the commit method each time an
  521. * an HTTP response is received from the server, it is responsible
  522. * for calling the user-supplied callbacks.
  523. *
  524. * Parameters:
  525. * resp - {<OpenLayers.Protocol.Response>}
  526. * options - {Object} The map of options passed to the commit call.
  527. */
  528. callUserCallback: function(resp, options) {
  529. var opt = options[resp.requestType];
  530. if(opt && opt.callback) {
  531. opt.callback.call(opt.scope, resp);
  532. }
  533. },
  534. CLASS_NAME: "OpenLayers.Protocol.HTTP"
  535. });