HTTP.js 19 KB

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