Web Api抛出500错误

时间:2017-12-14 18:45:50

标签: c# rest asp.net-web-api2 kendo-grid

我有一个Javascript Kendo Grid,我试图使用MVC Web Api进行服务器端排序。一切都在工作,除了排序。这是HTML代码:

<script type="text/javascript">
    $(function() {
        $("#gridTrades").kendoGrid({

            sortable: true,
            columns: [{ field: "tradeName", title: "Name", width: "250px" }, { field: "isActive", title: "Active", template: '#= isActive ? "<i class=\'fa fa-check\'></i>" : "" #', width: "100px" }],
            dataSource: {
                serverPaging: true,
                serverFiltering: false,
                serverSorting: true,
                pageSize: 3,
                schema: {
                    data: "data",
                    total: "total",
                    model: {
                        fields: {
                            id: { editable: false, type: "number" },
                            tradeName: { editable: false, type: "string" },
                            isActive: { editable: false, type: "boolean"}
                        }
                    }
                },
                batch: false,
                transport: {
                    read: {
                        url: "/api/v1/Trades"
                    }
                }
            }

        });
    });
</script>

这是我的Web Api代码:

[System.Web.Http.HttpGet]
[System.Web.Http.Route("api/v1/Trades")]
public IHttpActionResult GetTrades(int page = -1, int pageSize = -1, int skip = -1, int take = -1, string[][] sort = null)
{
    var trades = new BusinessLayer.VipScheduler.Trades();
    var total = BusinessLayer.VipScheduler.LoadData.FromSqlStatement<Models.Settings.GetTradesReturnModel>($"SELECT COUNT(*) AS Total FROM {BusinessLayer.VipScheduler.Trade.LgTableName}");

    trades.GetAll();

    var retval = new Models.Settings.GetTradesReturnModel {Total = total[0].Total};
    foreach (var trade in trades.OrderByDescending(z => z.IsActive).ThenBy(z => z.TradeName))
    {
        retval.Data.Add(new Models.Settings.GetTradesReturnModelData
        {
            Id = trade.TradeId,
            IsActive = trade.IsActive,
            TradeName = trade.TradeName
        });
    }

    return Ok(retval);
}

我遇到的问题是使用sort参数。如果我完全取消sort参数,Kendo Grid工作正常,除了排序不起作用。如果我按照上面的说法打开sort参数,我会收到500错误:

  

可选参数&#39; sort&#39; &#39; FormatterParameterBinding&#39;。

不支持

以下是Kendo Grid发送的请求:

GET /api/v1/Trades?take=3&skip=0&page=1&pageSize=3&sort%5B0%5D%5Bfield%5D=tradeName&sort%5B0%5D%5Bdir%5D=asc

任何有关使此排序参数起作用的帮助都将不胜感激!

2 个答案:

答案 0 :(得分:1)

为了防止您现在遇到错误 - 只需将sort参数设为非可选项(将其移至参数列表的开头并删除= null)。如果它无法绑定(例如,在查询字符串中未指定排序) - 它仍然会有null值,因此在这种情况下没有理由使= null默认。

现在,您的排序规范分为多个查询字符串参数:

sort[0][field‌​]=tradeName&sort[0][‌​dir]=asc

因此要将其绑定到模型,您需要一个自定义模型绑定器。首先创建表示排序规范的类:

public class KendoSortSpecifier {
    public string Field { get; set; }
    public string Direction { get; set; }
}

然后自定义模型绑定器(这只是一个示例,必要时根据您自己的需要调整):

public class KendoSortSpecifierBinder : System.Web.Http.ModelBinding.IModelBinder {
    private static readonly Regex _sortFieldMatcher;
    private static readonly Regex _sortDirMatcher;
    const int MaxSortSpecifiers = 5;
    static KendoSortSpecifierBinder() {
        _sortFieldMatcher = new Regex(@"^sort\[(?<index>\d+)\](\[field\]|\.field)$", RegexOptions.Singleline | RegexOptions.Compiled);
        _sortDirMatcher = new Regex(@"^sort\[(?<index>\d+)\](\[dir\]|\.dir)$", RegexOptions.Singleline | RegexOptions.Compiled);
    }

    public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext) {
        if (bindingContext.ModelType != typeof(KendoSortSpecifier[]))
            return false;
        var request = actionContext.Request;
        var queryString = request.GetQueryNameValuePairs();
        var result = new List<KendoSortSpecifier>();
        foreach (var kv in queryString) {
            var match = _sortFieldMatcher.Match(kv.Key);
            if (match.Success) {
                var index = int.Parse(match.Groups["index"].Value);
                if (index >= MaxSortSpecifiers)
                    continue;
                while (result.Count <= index) {
                    result.Add(new KendoSortSpecifier());
                }
                result[index].Field = kv.Value;
            }
            else {
                match = _sortDirMatcher.Match(kv.Key);
                if (match.Success) {
                    var index = int.Parse(match.Groups["index"].Value);
                    if (index >= MaxSortSpecifiers)
                        continue;
                    while (result.Count <= index) {
                        result.Add(new KendoSortSpecifier());
                    }
                    result[index].Direction = kv.Value;
                }
            }
        }
        bindingContext.Model = result.Where(c => !String.IsNullOrEmpty(c.Field)).ToArray();
        return true;
    }
}

最后你的控制器方法签名:

public IHttpActionResult GetTrades(
[System.Web.Http.ModelBinding.ModelBinder(typeof(KendoSortSpecifierBinder))]
    KendoSortSpecifier[] sort,
    int page = -1, 
    int pageSize = -1, 
    int skip = -1, 
    int take = -1)

现在你有一个强类型的排序模型(除了Direction可以用枚举而不是字符串表示),并且可以根据需要使用它来对数据进行排序。

答案 1 :(得分:0)

我建议你重构你的API方法。而不是给它提供路线参数,使用这样的DTO:

public IHttpActionResult GetTrades(GetTradesRequestDTO model)
{
    // method body
}

无论如何,这是最佳实践,并且模型绑定问题被最小化。此外,我不确定您为什么要为参数提供 -1 的默认值。而是使用从零开始的索引。这可能是个人偏好,但我不确定这些负面价值会带来什么。最后你的sort参数也需要一个重构器。它可以是字符串列表,也可以是强类型模型的集合,每个模型定义一个排序操作。即List<SortAction> sort;。您如何使用此信息并实施后端数据访问与此无关。

重构您的操作方法,模型绑定和参数数据类型应该可以解决您的问题。虽然我是根据经验编写的,但实际上并未在我的机器上测试过您的代码。