LINUX.ORG.RU

HTTP POST Content-Type: json vs x-www-form-urlencoded

 http post data,


0

1

Использую Catalyst::Action::Deserialize который должен обрабатывать каждый HTTP запрос (из POST, PUT, OPTIONS и DELETE) и десериализовать его данные в $c->req->data.

А еще есть хеш $c->req->params в который попадают GET параметры. Например если запрос был на URL вида http://myapp.org/method?foo=bar то $c->req->params будет выглядеть вот так:

{ foo => 'bar' }

Если при этом еще засунуть некие данные в тело HTTP запроса и поставить тип содержимого JSON, т.е.:

POST http://myapp.org/method?foo=bar
Content-Type: application/json

{ "baz": true }
То $c->req->data (десериализованные данные) примет вид:
{ baz => 1 }

Однако если послать запрос вида

POST http://myapp.org/method?foo=bar
Content-Type: application/x-www-form-urlencoded

baz=1
То $c->req->params примет вид:
{ foo => 'bar', baz => 1, }
а $c->req->data будет пуст.

Я так понимаю, что это сделано для совместимости отправки форм методами POST и GET. Это так?

Как уже было отмечено — моё приложение использует десериализацию данных запроса. И у меня чисто архитектурный вопрос:
Будет ли разумным соединять $c->req->params и $c->req->data и один хеш (params) если тело запроса JSON? Если да, то в каком порядке соединять эти хеши (как решать конфликты)?

Собственно почему возник вопрос:
Есть метод session/login который получает username + password. Хотелось бы в этом методе авторизовать не только по GET падаметрам а еще и по десериализованным параметрам (из JSON в теле запроса).

С другой стороны не хочется в каждом другом методе проверять одновременно и GET и тело запроса на присутствие параметров.

KennyMinigun ★★★★★
() автор топика
Последнее исправление: KennyMinigun (всего исправлений: 1)

Ого! Живой вебовый пёрл, не ожидал.

По теме: я думаю тут вопрос скорее идеологический. Я лично за архитекурное разделение контроллеров. Для отправки форм используется POST и x-www-form-urlencoded соотвественно. Если же запрос ajax, то тут правильно будет использовать json.

C1nde
()
Ответ на: комментарий от C1nde

Для отправки форм используется POST и x-www-form-urlencoded соотвественно. Если же запрос ajax, то тут правильно будет использовать json.

Т.е. для API метода session/login использовать x-www-form-urlencoded а для всех остальных json? (

* Потому, что login обычно осуществляют с формы, а всё остальное - ajax (это с браузера). А если допустить другой клиент — будет ли ему напряжно для login отправлять форму а для всего остального — json?

KennyMinigun ★★★★★
() автор топика
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от KennyMinigun

API метода session/login

если это API, то JSON.

А если допустить другой клиент — будет ли ему напряжно для login отправлять форму а для всего остального — json?

не распарсил вопрос

C1nde
()
Ответ на: комментарий от C1nde

Допустим напишу я Desktop клиент на Qt для моего приложения.

Для общения с сервером надо будет использовать API. Однако чтоб залогинится придётся отправлять форму, а потом дёргать методы используя JSON — получается некая путаница.

Ну да впринципе:

если это API, то JSON.

Может служить ответом на этот вопрос.

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Угу, в этом случае используй JSON.

Для общения с сервером надо будет использовать API. Однако чтоб залогинится придётся отправлять форму, а потом дёргать методы используя JSON — получается некая путаница.

ты немного неправильно понял API. Я делал авторизацию по токену те json'ом отправляется l/p, возвращается токен и он уже потом прикладывается к каждому последующему запросу в хедерах. Ну это я так делал, как совсем по-феншую не знаю.

C1nde
()
Ответ на: комментарий от C1nde

Я делал авторизацию по токену те json'ом отправляется l/p, возвращается токен и он уже потом прикладывается к каждому последующему запросу в хедерах. Ну это я так делал, как совсем по-феншую не знаю.

Так у меня аналогично: session/login принимает l/p и в случае удачного логина отправляет хедер Set-Cookie с session ID клиенту (т.е. последующие запросы должны содержать Session ID в Cookie)

KennyMinigun ★★★★★
() автор топика

Я так понимаю, что это сделано для совместимости отправки форм методами POST и GET.

Да. Т.к. в POST параметры QUERY_STRING не обязаны присутствовать.

Будет ли разумным соединять $c->req->params и $c->req->data и один хеш (params) если тело запроса JSON?

Без разницы. Для POST QUERY_STRING может быть без параметров, например:

POST http://myapp.org/
Параметров нет. Для GET все параметры передаются в QUERY_STRING. Поскольку в GET-запросы обычно не пихают json-строки, типа:
GET http://myapp.org/?json={"foo":true}
логично выбрать для себя какой-то один вариант (последний вариант тоже работоспособен). Потому что все зависит от движка сервера: ты можешь преобразовывать хоть в xml-деревья из чистого текста.

У каждого подхода есть минусы и плюсы. Когда параметры сериализуются из строки есть вероятность подсунуть искаженную json-строку, которая в итоге приведет к каким-то непредвиденным ошибкам. Также (для перла в особенности) необходимо контролировать максимальный размер входящих данных. JSON::XS может «всасывать» строки размеров в 2Гб. Несколько таких запросов и твой сервер вывалится с ошибкой out of memory (предварительно засвопив всю систему или будучи убитым oom-killer'ом). Но это легко обходится настройкой фронтенда, вроде nginx. В случае сериализации (JSON[::XS] в этом случае вообще не нужен) через HTTP::Body или аналогичный парсер QUERY_STRING, то есть вероятность засорить жесткий диск, ибо данные пишутся сначала туда, а потом от туда читаются. Я пользуюсь вторым вариантом, т.е. дергаю все из params. Но тут есть один ньюнс, сложные структуры вроде хэшы хэшей таким образом строятся особым образом и для этих целей Plack (PSGI) использует модуль Hash::MultiValue (я от него отказался в замен на упрощение API). Что это значит? Это значит, что допустим тебе надо получить нечто такое:

{"tree":{"one":"leaf","two":"leaf"}}
В итоге params (перловый хэш) будет выглядеть наподобии этого:
{
'tree[one]' => 'leaf',
'tree[two]' => 'leaf'
}
И вот тут-то Hash::MultiValue способен разруливать подобные неувязки. Или отказаться от такого и использовать одиночные вложенные массивы:
{"tree":['one','leaf','two','leaf'],"sub":[1,2,3]}
В перле через params они легко «воспримутся» в нормальные структуры.

С каталистом не работал, но думаю там все эти подводные камни уже кем-то решены. В Mojo это решено уже (о чем тебе и сказали выше, вроде вызовается через $req->params->json). А вообще, все это выглядит адскими костылями над непредназначенном для этого HTTP.

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 1)
Ответ на: комментарий от gh0stwizard

С каталистом не работал, но думаю там все эти подводные камни уже кем-то решены.

Да, в каталисте Action::Deserialize нормально отрабатывает все (указанные) виды HTTP запросов + десериализация по Content-Type.
Т.е. вот такой код будет нормально кушать JSON и YAML (пример HTTP POST запроса в шапке треда):

package MyApp::Controller;

BEGIN { extend Catalyst::Controller }

    __PACKAGE__->config(
        'default'   => 'text/x-yaml',
        'stash_key' => 'rest',
        'map'       => {
            'application/json'   => 'JSON',
            'text/x-jaml'        => 'YAML',
            # x-www-form-urlencoded явно не указан
        },
    );

sub begin :ActionClass('Deserialize') {}

sub method :Local {
    my ($self, $c) = @_;

    $c->req->data; # десериализованный JSON/YAML из тела POST
    $c->req->params; # десериализованные GET параметры + x-www-form-urlencoded POST данные
}

Тут вопрос скорее в удобстве: внутри method какие параметры мне считать основными: $c->req->data или $c->req->params?

Или стоит сказать соединять эти две структуры? т.е. аналогично:

use Hash::Merge qw(merge);

sub method :Local {
    my ($self, $c) = @_;

    # Только это делать на этапе десериализации
    # т.е. method будет получать все данные в одной структуре
    my $method_params = merge($c->req->params, $c->req->data);
}

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Одновременно они ($c->req->data и $c->req->params) врядли придут (но и такое может быть). Однако хотелось бы одинаково обрабатывать как GET так и POST данные.

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Ну напиши свой анализатор. Params для POST всегда будут содержать данные из data. Потестируй вариант когда будет один и тот же ключ и в запросе и в данных. Логично получить массив с обоими значениями или только одно из них. Я стремлюсь проверять входящие данные: мусор на входе, мусор на выходе.

gh0stwizard ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.