воскресенье, 27 марта 2011 г.

RESTful приложение на ExtJS 3



О том, что такое RESTful приложение и почему это круто можно почитать на Wiki, хабре или даже в статье ibm. Фактически, RESTful приложение - это приложение, построенное на вызове удаленных REST веб сервисов, выполняющих CRUD функции (Create, Read, Update, Delete - создать, прочитать, обновить и удалить соответственно). Каждый REST сервис характеризуется URI, по которому он вызывается, и некоторым набором принимаемых и возвращаемых параметров.

Особенность REST подхода состоит в активном использовании HTTP методов для выполнения CRUD функций: вместо использования URI адресов типа

/user/23/
/user/23/?action=delete
/user/23/?action=update


предлагается использовать стандартный адрес /user/23/, обращение к которому ведется с использованием HTTP команд GET, DELETE, PUT. Для создания нового элемента используется HTTP команда POST (в нашем примере к адресу /user/ - ID создаваемого элемента до момента создания мы, как правило, не знаем). Для приложений, функциональность которых не выходит за рамки CRUD, а можно уверенно сказать, что это большинство современных веб приложений, RESTful подход является лучшим выбором для создания веб сервисов сервисов и API.



В данной статье я предлагаю рассмотреть создание простейшего RESTful приложения с использованием ExtJS 3 на клиентской стороне. Серверную часть можно написать на любой любимой технологии - будь то Java, PHP, Python или что-то еще.

В этой статье подразумевается, что Ext уже подключен, также рекомендуется использование связки Firefox + Firebug для отладки Javascript и просмотра http заголовков запросов и ответов. Итак, поехали!

Будем использовать хранилище (Store) с настроенным HttpProxy. Дело в том, что HttpProxy поддерживает api для установки методов загрузки, обновления и удаления данных в хранилищах Store:

var myProxy = new Ext.data.HttpProxy({
    api: {
        read:    {url: 'app/users/', method: 'GET'},
        create:  {url: 'app/users/', read: 'POST'},
        update:  {url: 'app/users/', method: 'PUT'},
        destroy: {url: 'app/users/', method: 'DELETE'}
    }
});

Как видим, можно настроить разные URI адреса для действий чтения, обновления и т.д., однако это плохо согласуется с идеологией REST, поэтому мы будем использовать один адрес с разными передаваемыми параметрами и разными HTTP методами. Стоит отметить, что по умолчанию используется метод POST, поэтому в каждом случае следует вручную указывать параметр method.

Сконфигурируем JsonReader и JsonWriter:

var myReader = new Ext.data.JsonReader({
    successProperty: 'success', // json элемент со статусом
                                // результата (false or true)
    idProperty: 'id',
    root: 'data', // корневой элемент с данными
}, [
    {name: 'id'},
    {name: 'email', allowBlank: false},
    {name: 'first', allowBlank: false},
    {name: 'last', allowBlank: false}
]);

var myWriter = new Ext.data.JsonWriter({
    encode: true,         // важно! кодировать Store
    writeAllFields: false // сохранять только измененные поля
});

Теперь можно сконфигурировать само хранилище. Фактически, мы указываем здесь reader, writer и proxy:

var myStore = new Ext.data.Store({
    id: 'user',
    proxy: myProxy,
    reader: myReader,
    writer: myWriter,
    idProperty: 'id', // важно! Id элементов хранилища
    autoSave: true // при изменении хранилища будет автоматически
                   // сформирован запрос на его сохранение
});

Теперь при изменении хранилища - то есть изменении записей в хранилище - будет автоматически посылаться запрос к серверу для сохранения всех модификаций. Если по каким-то причинам мы хотим отправлять запрос на сохранение текущего состояния хранилища сами, необходимо установить конфигурационный параметр autoSave хранилища в false; в таком случае запрос на сохранение будет сформирован при вызове метода save() хранилища:

myStore.save();

Обратите внимание на параметр idProperty хранилища; в случае некорректной установки этого параметра при сохранении измененного элемента все равно будет создан запрос на создание нового элемента вместо сохранения существующего (POST вместо PUT). Это связано с тем, что каждая запись в хранилище однозначно идентифицируется параметром id, который должен быть связан с реальным идентификатором нашей записи (id в JsonReader - как правило, идентификатор записи в базе данных).

Также обратите внимание, что если используется mapping в параметрах fields для JsonReader - в idProperty нужно указывать реальное название параметра в Json данных, а не название колонки данных в Ext'е. Для тех, кто не в курсе - маппинг это сопоставление имен параметров в Json данных и имен этих же параметров в Ext для манипуляции с ними, при желании можно задавать разные имена. Иными словами, мы могли бы так настроить JsonReader:

var myReader = new Ext.data.JsonReader({
    successProperty: 'success', // json элемент со статусом
                                // результата (false or true)
    idProperty: 'id',
    root: 'data', // корневой элемент с данными
}, [
    {name: 'ExtIdentificator', mapping: 'realJsonId'},
    {name: 'email', allowBlank: false},
    {name: 'first', allowBlank: false},
    {name: 'last', allowBlank: false}
]);

Для idParamater в таком случае надо указывать realJsonId.

Итак, теперь мы умеем сохранять данные хранилища. Но как модифицировать эти данные? Как правило, хранилище используется либо для загрузки данных с сервера с последующим использованием в формах или других элементах страницы, либо для загрузки данных в таблицу Grid. В случае использования EditorGridPanel данные таблицы и данные хранилища полностью синхронизированы, то есть при изменении данных таблицы автоматически меняется и хранилище, с ним связанное. Это первый способ изменения данных хранилища.

Другой способ - ручной. Например, мы хотим обновить данные первого пользователя в хранилище myStore:

myStore.getAt(0).data.items[0].name = 'Alexey';

myStore.getAt(0) возвращает первую запись (Record) хранилища; свойство data записи хранит в себе все данные записи.

Таким образом, изменив одним из описанных способов хранилище, мы имеем возможность сохранить новое состояние хранилища либо автоматически (путем настройки хранилища autoSave: true), либо вручную путем вызова метода хранилища save().

Пример использования JsonWriter можно посмотреть здесь.

Комментариев нет:

Отправить комментарий