PDA

View Full Version : [OPEN-1105] DELETE in RESTful mode sending entity body



facorreia
6 Jul 2010, 11:11 AM
Ext version tested:

Ext 3.2 rev 1


Adapter used:

ext


css used:

custom css - restful.css, examples.css and silk.css from the RESTful example page


Browser versions tested against:

Firefox 3.6.6 on Ubuntu with Firebug 1.5.4


Operating System:

Ubuntu 10.4


Description:

The DELETE operation in RESTful mode is sending the correct HTTP verb (DELETE) but it is also sending a message body. Although not explicitly forbidden by the HTTP specification, this message body is not an usual part of a RESTful protocol. It is redundant, since the information it is sending (the record ID) is already in the resource URL. And some web servers, notably Google App Engine, will deny such requests with error code 400 Bad Request.


Test Case:

This behavior can be seen in the default RESTful example.

restful.html:


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>RESTful Store Example</title>

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />

<script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));</script><script type="text/javascript">try{var pageTracker = _gat._getTracker("UA-1396058-1");pageTracker._initData();pageTracker._trackPageview();} catch(err) {}</script>
<!-- LIBS -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>

<!-- ENDLIBS -->

<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="../shared/extjs/App.js"></script>
<script type="text/javascript" src="restful.js"></script>
<script type="text/javascript" src="../ux/RowEditor.js"></script>

<link rel="stylesheet" type="text/css" href="restful.css" />

<link rel="stylesheet" href="../ux/css/RowEditor.css" />

<!-- Common Styles for the examples -->
<link rel="stylesheet" type="text/css" href="../shared/examples.css" />
<link rel="stylesheet" type="text/css" href="../shared/icons/silk.css" />
</head>
<body>
<script type="text/javascript" src="../shared/examples.js"></script><!-- EXAMPLES -->
<h1>RESTful Store Example</h1>
<p>This example shows how to implement a RESTful Store. A Store is made RESTful by simply setting the new configuration-property <strong><i>restful: true</i></strong>

and plugging a suitable <strong>Ext.data.DataWriter</strong> into your Store. This example uses <strong>Ext.data.JsonWriter</strong>.</p>

<p>Note that the js is not minified so it is readable. See <a href="restful.js">restful.js</a></p>

<p>Take note of the requests being generated in Firebug as you interact with the Grid.</p>

<p>The HttpProxy in this example points to a single url <strong>app.php/users</strong>. You may have to edit your web-server's configuration to allow the
php back-end to be executable. app.php implements a simple RESTful backend controller and simulates a database by storing records in the $_SESSION.</p>

<code><pre>
var proxy = new Ext.data.HttpProxy({
url: 'app.php/users'
});

</pre></code>
<div class="container" style="width:500px">
<div id="user-grid"></div>
</div>

</body>
</html>




/*!
* Ext JS Library 3.2.1
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
// Application instance for showing user-feedback messages.
var App = new Ext.App({});

// Create a standard HttpProxy instance.
var proxy = new Ext.data.HttpProxy({
url: 'app.php/users'
});

// Typical JsonReader. Notice additional meta-data params for defining the core attributes of your json-response
var reader = new Ext.data.JsonReader({
totalProperty: 'total',
successProperty: 'success',
idProperty: 'id',
root: 'data',
messageProperty: 'message' // <-- New "messageProperty" meta-data
}, [
{name: 'id'},
{name: 'email', allowBlank: false},
{name: 'first', allowBlank: false},
{name: 'last', allowBlank: false}
]);

// The new DataWriter component.
var writer = new Ext.data.JsonWriter({
encode: false // <-- don't return encoded JSON -- causes Ext.Ajax#request to send data using jsonData config rather than HTTP params
});

// Typical Store collecting the Proxy, Reader and Writer together.
var store = new Ext.data.Store({
id: 'user',
restful: true, // <-- This Store is RESTful
proxy: proxy,
reader: reader,
writer: writer // <-- plug a DataWriter into the store just as you would a Reader
});

// load the store immeditately
store.load();

////
// ***New*** centralized listening of DataProxy events "beforewrite", "write" and "writeexception"
// upon Ext.data.DataProxy class. This is handy for centralizing user-feedback messaging into one place rather than
// attaching listenrs to EACH Store.
//
// Listen to all DataProxy beforewrite events
//
Ext.data.DataProxy.addListener('beforewrite', function(proxy, action) {
App.setAlert(App.STATUS_NOTICE, "Before " + action);
});

////
// all write events
//
Ext.data.DataProxy.addListener('write', function(proxy, action, result, res, rs) {
App.setAlert(true, action + ':' + res.message);
});

////
// all exception events
//
Ext.data.DataProxy.addListener('exception', function(proxy, type, action, options, res) {
App.setAlert(false, "Something bad happend while executing " + action);
});

// Let's pretend we rendered our grid-columns with meta-data from our ORM framework.
var userColumns = [
{header: "ID", width: 40, sortable: true, dataIndex: 'id'},
{header: "Email", width: 100, sortable: true, dataIndex: 'email', editor: new Ext.form.TextField({})},
{header: "First", width: 50, sortable: true, dataIndex: 'first', editor: new Ext.form.TextField({})},
{header: "Last", width: 50, sortable: true, dataIndex: 'last', editor: new Ext.form.TextField({})}
];


Ext.onReady(function() {
Ext.QuickTips.init();

// use RowEditor for editing
var editor = new Ext.ux.grid.RowEditor({
saveText: 'Update'
});

// Create a typical GridPanel with RowEditor plugin
var userGrid = new Ext.grid.GridPanel({
renderTo: 'user-grid',
iconCls: 'icon-grid',
frame: true,
title: 'Users',
autoScroll: true,
height: 300,
store: store,
plugins: [editor],
columns : userColumns,
tbar: [{
text: 'Add',
iconCls: 'silk-add',
handler: onAdd
}, '-', {
text: 'Delete',
iconCls: 'silk-delete',
handler: onDelete
}, '-'],
viewConfig: {
forceFit: true
}
});

/**
* onAdd
*/
function onAdd(btn, ev) {
var u = new userGrid.store.recordType({
first : '',
last: '',
email : ''
});
editor.stopEditing();
userGrid.store.insert(0, u);
editor.startEditing(0);
}
/**
* onDelete
*/
function onDelete() {
var rec = userGrid.getSelectionModel().getSelected();
if (!rec) {
return false;
}
userGrid.store.remove(rec);
}

});


See this URL : http://www.sencha.com/deploy/dev/examples/restful/restful.html

Steps to reproduce the problem:

Open the Firebug console
Open the sample page
Click on a record in the grid
Click on the DELETE button
Look at the DELETE request in the Firebug console


The result that was expected:

The request should not contain an entity body; especifically, the request header should not have a Content-Length entry.


The result that occurs instead:

The DELETE request contains an entity body in the form {"data":n} and a Content-Length header entry e.g. Content-Length 10.


Screenshot or Video:

attached


Debugging already done:

Used the Firebug debugger to follow the chain of calls, through the HttpProxy. I saw there is some especial treatment for RESTful stores. I tried modifying some objects, for instance setting jsonData to null on the delete event, but I never could disable sending some entity body, and I also couldn't find a way to do that.
Requests for a workaround in the Sencha Forum (http://www.sencha.com/forum/showthread.php?102849-RESTful-DELETE-request-without-entity-body&p=482325) and on Stack Overflow (http://stackoverflow.com/questions/3119179/how-can-i-prevent-ext-js-from-including-an-entity-body-in-delete-requests-using-a) came up empty.


Possible fix:

a) Just treat the special case of the DELETE verb in RESTful mode, and don't send an entity body in this case unless some configuration option explicitly forces it.
b) Allow for an override in HttpProxy where this situation can be checked for, so a custom RESTful HttpProxy can be written that is more conformant to RESTful techniques and to the HTTP specification.

facorreia
12 Jul 2010, 5:08 AM
Additional information: This answer (http://stackoverflow.com/questions/3119179/how-can-i-prevent-ext-js-from-including-an-entity-body-in-delete-requests-using-a/3196990#3196990) at StackOverflow shows how the default HttpProxy can be changed to avoid the redundant message body and be more conformant to the semantic of the DELETE verb. Tested with Google App Engine.