24

在使用 RESTful API 的现代 Web 项目中,我们经常看到像下面这样的 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 调用的包装器。我想知道你是否真的可以生成这些包装器。从本质上讲,我将为我的 WebAPI 生成一个基于 JS 的客户端,类似于 Java 和 .NET 如何生成基于 WSDL 的客户端。

  1. 以前有这样做过吗?
  2. 是否有其他方法可以将 ASP.NET WebAPI 和 JavaScript 绑定在一起,而无需编写一堆 AJAX 样板代码?
  3. 换句话说,是否有用于创建基于服务器端接口(如 ASP.NET WebAPI)的 JS 接口的框架?

我已经看过 amplifyJS 但这只是部分解决了问题。我正在寻找一种解决方案,该解决方案实际上基于我的解决方案中的 WebAPI 控制器创建接口。如果这不存在,我将开始修补自己。我已经有了一个WebAPIClientGenerator使用反射来遍历所有ApiController's 的想法。

4

3 回答 3

31

刚找到一个项目叫:ProxyApi

ProxyApi 是一个自动为您的 ASP.NET MVC 和 WebApi 控制器创建 JavaScript 代理对象的库。

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

博客:http: //blog.greatrexpectations.com/2012/11/06/proxyapi-automatic-javascript-proxies-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 开发错误。

于 2013-08-13T20:05:08.703 回答
4

我正在为 .NET 开发 Swagger 开源工具链NSwag:使用此工具,您可以为单个或多个 Web API 控制器生成 TypeScript 客户端

在 UI 中

  1. 选择 Web API DLL
  2. 选择控制器类
  3. 生成 TypeScript 客户端代码(在您的情况下,选择JQueryCallbacksorJQueryPromises模板)

看看http://nswag.org

仅供参考:TypeScript是一种被转译为 JavaScript 的语言

于 2016-06-04T17:59:13.307 回答
1

这个优秀的另一个项目允许你做你所要求的。该项目为 MVC 和 WebApi 控制器自动生成 JavaScript 代理。而且,该项目涵盖了 WebApi 功能,例如自定义 ActionName 属性。通过这个项目,您还将拥有 Intellisense。
http://jsnet.codeplex.com/

智能感知示例

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);

});
}
于 2016-07-30T09:59:23.193 回答