基于ASP.NET WebAPI控件生成JS客户端

Mar*_*ers 24 javascript asp.net rest proxy-classes asp.net-web-api

在使用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
});
Run Code Online (Sandbox Code Playgroud)

我喜欢WebAPI,我喜欢Knockout,我喜欢将两者结合在一起.然而,这些AJAX调用非常冗长,包含了我并不感兴趣的各种细节.相反,我创建了一个围绕这些方法的包装器:

myapp.api.saveNote(note)
Run Code Online (Sandbox Code Playgroud)

但是,这仍然需要我实际编写一个包含AJAX调用的包装器.我想知道你是否真的可以生成这些包装器.本质上,我将为我的WebAPI生成一个基于JS的客户端,类似于Java和.NET如何基于WSDL生成客户端.

  1. 以前做过吗?
  2. 有没有其他方法可以将ASP.NET WebAPI和JavaScript结合在一起,而无需编写大量的AJAX样板代码?
  3. 换句话说,是否存在基于ASP.NET WebAPI等服务器端接口创建JS接口的框架?

我已经看过amplifyJS,但这只能部分地解决问题.我正在寻找一种解决方案,它实际上在我的解决方案中基于WebAPI控制器创建了一个接口.如果这不存在,我会开始修补自己.我已经有了一个想法,WebAPIClientGenerator它使用反射迭代所有ApiController的.

Mar*_*ers 30

刚刚找到一个叫做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();
Run Code Online (Sandbox Code Playgroud)

根据输出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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将此控制器与以下视图结合使用:

@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>
Run Code Online (Sandbox Code Playgroud)

控制器将使用它ApiExplorer来生成有关所有可用WebAPI操作的元数据.该视图将此数据呈现为JSON,然后执行一些JavaScript以将此数据转换为实际的可执行JavaScript函数.

要使用这一点魔力,请 jQuery引用之后在Layout页面的头部插入以下行.

@Html.Action(MVC.Metadata.WebApiDescription())
Run Code Online (Sandbox Code Playgroud)

从现在开始,您可以使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       
});
Run Code Online (Sandbox Code Playgroud)

这个简单的代理将自动区分查询字符串参数和请求主体参数.缺少参数或多个正文参数将生成错误以防止拼写错误或其他常见的WebAPI开发错误.

  • 这是我见过的最好的答案之一.非常感谢. (3认同)