Генерация JS-клиента на базе ASP.NET WebAPI Controller

В современных веб-проектах, использующих RESTful API, мы часто видим AJAX-вызовы, подобные приведенному ниже, разбросанные по нашим JavaScript-файлам.

$.ajax({
    type: "POST",
    url: myapp.baseUrl + 'Api/Note',
    data: ko.mapping.toJSON(note),
    contentType: 'application/json',
}).done(function (response) {
    // do something
}).fail(function (jqxhr) {
    // do something else
});

Я люблю WebAPI, люблю Knockout и люблю связывать их вместе. Однако эти AJAX-вызовы довольно многословны и содержат всевозможные детали, которые меня не очень интересуют. Поэтому вместо этого я создаю обертку вокруг этих методов:

myapp.api.saveNote(note)

Однако это по-прежнему требует, чтобы я написал оболочку, содержащую вызов AJAX. Мне было интересно, можете ли вы на самом деле генерировать эти оболочки. По сути, я буду генерировать клиент на основе JS для моего WebAPI, аналогично тому, как Java и .NET могут генерировать клиентов на основе WSDL.

  1. Делалось ли это раньше?
  2. Существуют ли другие способы связать ASP.NET WebAPI и JavaScript вместе без написания кучи шаблонного кода AJAX?
  3. Другими словами, существуют ли фреймворки для создания JS-интерфейсов на основе серверных интерфейсов вроде ASP.NET WebAPI?

Я уже смотрел на amplifyJS, но это лишь частично решает проблему. Я ищу решение, которое фактически создает интерфейс на основе контроллеров WebAPI в моем решении. Если этого не будет, я начну ковыряться сам. У меня уже есть идея для WebAPIClientGenerator, который использует отражение для перебора всех ApiController.


person Martin Devillers    schedule 13.08.2013    source источник


Ответы (3)


Только что нашел проект под названием ProxyApi.

ProxyApi — это библиотека, которая автоматически создает прокси-объекты JavaScript для ваших контроллеров ASP.NET MVC и WebApi.

GitHub: https://github.com/stevegreatrex/ProxyApi

Блог: http:// blog.greatreexpectations.com/2012/11/06/proxyapi-automatic-javascript-proxy-for-webapi-and-mvc/

ProxyApi сгенерировал недопустимый JavaScript для моего решения, которое содержало более сотни отдельных действий WebAPI. Вероятно, это связано с тем, что ProxyApi не охватывает все функции WebApi, такие как настраиваемые атрибуты ActionName. Кроме того, на мой вкус, библиотека ProxyApi немного громоздка. Должен быть более эффективный способ сделать это...

Поэтому я решил взглянуть на исходный код ASP.NET WebAPI, и оказалось, что WebAPI имеет встроенную функциональность с самоописанием. Вы можете использовать следующий код из любого места решения ASP.NET для доступа к метаданным WebAPI:

var apiExplorer = GlobalConfiguration.Configuration.Services.GetApiExplorer();

Основываясь на выводе apiExplorer.ApiDescriptions, я накатил свой собственный поставщик метаданных:

public class MetadataController : Controller
{
    public virtual PartialViewResult WebApiDescription()
    {
        var apiExplorer = GlobalConfiguration.Configuration.Services.GetApiExplorer();
        var apiMethods = apiExplorer.ApiDescriptions.Select(ad => new ApiMethodModel(ad)).ToList();
        return PartialView(apiMethods);
    }

    public class ApiMethodModel
    {
        public string Method { get; set; }
        public string Url { get; set; }
        public string ControllerName { get; set; }
        public string ActionName { get; set; }
        public IEnumerable<ApiParameterModel> Parameters { get; set; }

        public ApiMethodModel(ApiDescription apiDescription)
        {
            Method = apiDescription.HttpMethod.Method;
            Url = apiDescription.RelativePath;
            ControllerName = apiDescription.ActionDescriptor.ControllerDescriptor.ControllerName;
            ActionName = apiDescription.ActionDescriptor.ActionName;
            Parameters = apiDescription.ParameterDescriptions.Select(pd => new ApiParameterModel(pd));
        }
    }

    public class ApiParameterModel
    {
        public string Name { get; set; }
        public bool IsUriParameter { get; set; }

        public ApiParameterModel(ApiParameterDescription apiParameterDescription)
        {
            Name = apiParameterDescription.Name;
            IsUriParameter = apiParameterDescription.Source == ApiParameterSource.FromUri;
        }
    }
}

Используйте этот контроллер в сочетании со следующим представлением:

@model IEnumerable<Awesome.Controllers.MetadataController.ApiMethodModel>
<script type="text/javascript">
    var awesome = awesome || {};

    awesome.api = {
        metadata: @Html.Raw(Json.Encode(Model))
    };

    $.each(awesome.api.metadata, function (i, action) {
        if (!awesome.api[action.ControllerName]) {
            awesome.api[action.ControllerName] = {};
        }
        awesome.api[action.ControllerName][action.ActionName] = function (parameters) {
            var url = '/' + action.Url;
            var data;
            $.each(action.Parameters, function (j, parameter) {
                if (parameters[parameter.Name] === undefined) {
                    console.log('Missing parameter: ' + parameter.Name + ' for API: ' + action.ControllerName + '/' + action.ActionName);
                } else if (parameter.IsUriParameter) {
                    url = url.replace("{" + parameter.Name + "}", parameters[parameter.Name]);
                } else if (data === undefined) {
                    data = parameters[parameter.Name];
                } else {
                    console.log('Detected multiple body-parameters for API: ' + action.ControllerName + '/' + action.ActionName);
                }
            });
            return $.ajax({
                type: action.Method,
                url: url,
                data: data,
                contentType: 'application/json'
            });
        };
    });
</script>

Контроллер будет использовать ApiExplorer для создания метаданных обо всех доступных действиях WebAPI. Представление отобразит эти данные как JSON, а затем выполнит некоторый JavaScript для преобразования этих данных в реальные исполняемые функции JavaScript.

Чтобы использовать это небольшое волшебство, вставьте следующую строку в заголовок страницы макета после ссылки на jQuery.

@Html.Action(MVC.Metadata.WebApiDescription())

Отныне вы можете сделать так, чтобы ваши вызовы WebAPI выглядели так:

// GET: /Api/Notes?id={id}
awesome.api.Notes.Get({ id: id }).done(function () {
    // .. do something cool       
});

// POST: /Api/Notes
awesome.api.Notes.Post({ form: formData }).done(function () {
    // .. do something cool       
});

Этот простой прокси-сервер автоматически отличает параметры строки запроса от параметров тела запроса. Отсутствующие параметры или несколько параметров тела вызовут ошибку, чтобы предотвратить опечатки или другие распространенные ошибки разработки WebAPI.

person Martin Devillers    schedule 13.08.2013
comment
Как мне получить список, используя метод Get? Похоже, Get всегда ожидает параметр ID - person To Ka; 23.10.2013
comment
Я понял это, я просто добавил оператор if (typeof parameters != 'undefined' && parameters != null) { вокруг блока проверки параметров и добавил url = url.replace(""{"" + parameter.Name + ""}"", ''); в остальную часть - person To Ka; 23.10.2013
comment
Это один из лучших ответов, которые я когда-либо видел на SO. Огромное спасибо. - person Bryan Slatner; 08.05.2014
comment
Где MVC определено в MVC 4? - person Alex Wiese; 29.09.2014
comment
MVC.Metadata.WebApiDescription() генерируется T4MVC. Если вы не используете T4MVC в своем проекте, вам необходимо указать строку, указывающую на ваше действие. Например. @Url.Action("~/Metadata/WebApiDescription") - person Martin Devillers; 29.09.2014
comment
для запросов POST и PUT данные должны быть преобразованы в строку с использованием JSON.stringify - person uzul; 09.06.2016

Я работаю над цепочкой инструментов Swagger с открытым исходным кодом NSwag для .NET: с помощью этого инструмента вы можете создать клиент TypeScript для одного или нескольких контроллеров веб-API.

В интерфейсе просто

  1. Выберите библиотеку DLL веб-API.
  2. Выберите классы контроллера
  3. Сгенерируйте код клиента TypeScript (в вашем случае выберите шаблон JQueryCallbacks или JQueryPromises)

Посмотрите на http://nswag.org.

К вашему сведению: TypeScript — это язык, который транспилируется в JavaScript.

person Rico Suter    schedule 04.06.2016

Этот отличный еще один проект позволяет вам делать то, что вы просили. Этот проект автоматически создает прокси-серверы JavaScript для контроллеров MVC и WebApi. И этот проект охватывает функции WebApi, такие как настраиваемые атрибуты ActionName. С этим проектом у вас также будет Intellisense.
http://jsnet.codeplex.com/

Пример Intellisense

window.test = function test() {
/// <summary>
///This example works.
///You have the Intellisense. It's great!!!
///No hard coded url.
///</summary>

//-- settings of ajax request.
var a = $dpUrlSet.Customer.Create.$action0.$AjaxSettings();

//-- your parameters of action method
a.data.name = "Scott Gu";
a.data.address = "Somewhere in Redmond";

//-- stringify
a.data = JSON.stringify(a.data);

//-- send ajax request
var xhr = $.ajax(a);

xhr.success(function (id) {
    /// <summary>Response of ajax request</summary>

    //-- settings of ajax request.
    var a = $dpUrlSet.Customer.Update.$action0.$AjaxSettings();

    //-- your parameters of action method
    a.data.id = id;
    a.data.name = "Scott Gu";
    a.data.address = "Somewhere in Seattle";

    //-- stringify
    a.data = JSON.stringify(a.data);

    //-- send ajax request
    var xhr = $.ajax(a);

});
}
person Alexandre Tran    schedule 30.07.2016