Представьте, есть две комманды разработчиков: одни пишут на C#, другие — на Эрланге.
Первые хотят использовать функции из продуктов вторых.
Отлично, надо договориться об API, реализовать его и выдать документацию Шарповым ребятам.
На основе чего будет реализован этот API?
Подумав, пришли к выбору между SOAP и REST.
Поговорим о поддержке данных технологий нашими платформами.
C REST всё понятно: нужны инструменты для работы с HTTP и XML/JSON.
А как обстоят дела с SOAP?
На стороне .Net присутствует хорошая поддержка и client-side, и server-side: System.Web.Services.dll.
Всё несколько сложнее с server-side Эрланга…
Как это обычно делают?
Открываем мануал по YAWS и видим, что сначала надо написать WSDL. Потом нужно скормить этот WSDL модулю yaws_soap_lib: он сделает hrl-файл. Потом надо написать хендлер, который будет обрабатывать примитивы, описанные в этом hrl-файле.
На мой взгляд, это несколько неудобно: сначала WSDL, потом — код. Написать WSDL с первого раза сможет далеко не каждый. А при внесении изменений в API нужно подгонять код сервиса под получившиеся автогенерированные структуры данных.
Да и вообще, как-то всё должно быть проще, не так ли?
Хочется, чтобы, как в System.Web.Services.dll: пишешь сервис, помечаешь, что должно быть доступно извне — получаешь WSDL.
Как бы мне хотелось это делать?
Во-первых, встраиваемый cowboy лично мне нравится гораздо больше, чем stand-alone Yaws, который с основным приложением надо будет как-то связывать.
Во-вторых, хочется, всё таки, просто писать код, а WSDL пусть генерирует кто-нибудь другой.
На пример, вот так (auth_ws.erl):
-module(auth_ws).
-compile({parse_transform, durden_pt}).
-include_lib("durden/include/predefined_types.hrl").
-soap_target_ns("http://rgafiyatullin.github.com/habr/auth-ws/v0").
-soap_service_name("AuthAPI").
-soap_actions([
'Authenticate'/2
]).
-record('User', {
'Valid' :: boolean(),
'ID' :: uuid(),
'DisplayName' :: string()
}).
user_new() ->
#'User' {
'Valid' = false,
'ID' = "00000000-0000-0000-0000-000000000000",
'DisplayName' = ""
}.
-spec 'Authenticate'( Name :: string(), PW :: string() ) -> #'User'{}.
'Authenticate'(Name, PW) ->
case {Name, PW} of
{"RG", "pw#here"} ->
( user_new() ) #'User' {
'Valid' = true,
'ID' = "f47ac10b-58cc-4372-a567-0e02b2c3d479",
'DisplayName' = "Roma"
};
_ ->
user_new()
end.
Что происходит:
- Написали модуль
- Пометили функции, которые открываем для внешнего мира
- Позволили трансформации 'durden_pt' проанализировать типы публичных вызовов и составить заготовку для генерации WSDL
Подключаем к cowboy
-module(cowboy_dispatch).
-compile(export_all).
'Test'() -> ok.
start_cowboy() ->
Dispatch = [
{'_', [
{[<<"habr">>, <<"auth.asmx">>, '...'], auth_ws, [ "http://localhost:8080/habr/auth.asmx" ] }
]}
],
cowboy:start_listener(my_http_listener, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
).
Используем сервис
using System;
using Auth.Client;
namespace RG.Habr.Durden {
public class ClientProgram {
private static string VarDump( User u ) {
if (u.Valid) {
return String.Format(
"{2}{{ ID : '{0}', DisplayName : '{1}' }}",
u.ID, u.DisplayName, u.GetType()
);
}
else {
return String.Format("{0}{{ Unauthorized }}", u.GetType());
}
}
public static int Main(string[] args) {
var authAPI = new AuthAPI();
var authedUser = authAPI.Authenticate("RG", "pw#here");
var unAuthedUser = authAPI.Authenticate("Stranger", "bad pw");
Console.WriteLine(VarDump(authedUser));
Console.WriteLine(VarDump(unAuthedUser));
return 0;
}
}
}
rg@aluminumcan [cs-example] * master > make && mono bin/auth_client.exe
wsdl
-n:Auth.Client
-out:src/Auth.Client.cs
'http://localhost:8080/habr/auth.asmx?wsdl=0'
Web Services Description Language Utility
Mono Framework v4.0.30319.1
Writing file 'src/Auth.Client.cs'
dmcs
-r:System.dll
-r:System.Web.Services.dll
-target:exe -out:bin/auth_client.exe
src/Auth.Client.cs
src/ClientMain.cs
Auth.Client.User{ ID : 'f47ac10b-58cc-4372-a567-0e02b2c3d479', DisplayName : 'Roma' }
Auth.Client.User{ Unauthorized }
Заключение
Взять durden можно у меня на GitHub.
К сожалению, на данный момент (2012-09-05) есть некоторые ограничения:
- не поддерживаются умолчальные значения
(http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383525) - не поддерживаются сериализация чисел c плавающей точкой/запятой
(http://www.w3.org/TR/xmlschema-2/#float) - присутствует только транспорт (protocol binding) SOAP/1.2: нет HTTP-POST, HTTP-GET, SOAP/1.1
Решение упомянутых проблем запланировано в том порядке, в котором они перечислены.
Как и раньше, конструктивной критике и, тем паче, коллаборации — Ура!
* Tyler: «Tonight? We make soap.»
Автор: RomkoGoofique