如何在asp.net核心web api中绑定Json Query字符串

时间:2017-06-17 18:21:21

标签: c# asp.net-mvc asp.net-web-api asp.net-core

以下代码在asp.net web API中工作正常但在Asp.net核心中不起作用。

端点api/devices?query={"deviceName":"example"}

[HttpGet]
public Device ([FromUri] string deviceName)
{        
        var device = context.Computers.Where(x => x.deviceName == deviceName);
        return device;
}

[FromUri]属性不存在asp.net核心网API,我尝试使用以下内容,但没有成功。

[HttpGet]
public Device  Get([FromQuery] string  deviceName)
{
    return repo.GetDeviceByName(deviceName);
}

2 个答案:

答案 0 :(得分:4)

不幸的是,没有办法像你那样在GET查询中绑定JSON。您正在寻找的是使用自定义模型绑定器告诉ASP.net Core您想要绑定的方式。

首先,您要为JSON对象构建模型。

public class MyCustomModel
{
    public string DeviceName { get; set; }
}

接下来,您需要构建模型绑定器。下面给出一个简单的例子,但你显然想要进行其他检查是否可以转换,Try / Catch块等。本质上,模型绑定器告诉ASP.net Core应该如何绑定模型。您可能还遇到了类型的TypeConverters,如何在模型绑定期间将其更改为其他类型。现在让我们使用modelbinders。

public class MyViewModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var jsonString = bindingContext.ActionContext.HttpContext.Request.Query["query"];
        MyCustomModel result = JsonConvert.DeserializeObject<MyCustomModel>(jsonString);

        bindingContext.Result = ModelBindingResult.Success(result);
        return Task.CompletedTask;
    }
}

所以我们所做的就是获取查询字符串并将其反序列化为我们的模型。

接下来我们建立一个提供者。提供者告诉ASP.net核心使用哪个模型绑定器。在我们的例子中,它很简单,如果模型类型是我们的自定义类型,那么使用我们的自定义绑定器。

public class MyViewModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType == typeof(MyCustomModel))
            return new MyViewModelBinder();

        return null;
    }
}

最后一块拼图。在我们的startup.cs中,我们找到了添加MVC服务的位置,并将模型绑定器插入到列表的前面。这个很重要。如果我们只是将我们的模型绑定器添加到列表中,另一个模型绑定器可能会认为应该使用它(First in first served),所以我们可能永远不会将它变为我们的。所以一定要在开始时插入它。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(config => config.ModelBinderProviders.Insert(0, new MyViewModelBinderProvider()));
}

现在我们只创建一个我们读取数据的动作,不需要任何属性。

[HttpGet]
public void Get(MyCustomModel model)
{

}

进一步阅读:

答案 1 :(得分:0)

我提出这个问题是因为我有类似的问题。 在我的场景中,实现 ModelBinderAttribute 更方便:

public class FromUriJsonAttribute : ModelBinderAttribute
{
    public FromUriJsonAttribute()
    {
        BinderType = typeof(JsonBinder);
    }

    public FromUriJsonAttribute(string paramName)
        : this()
    {
        Name = paramName;
    }
}

在@MindingData 回答中需要活页夹:

public class JsonBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        try
        {
            var name = bindingContext.ModelName;
            var json = actionContext.Request.GetQueryNameValuePairs().FirstOrDefault(x => x.Key == name).Value;
            var targetType = bindingContext.ModelType;
            var model = Newtonsoft.Json.JsonConvert.DeserializeObject(json, targetType);
            bindingContext.Model = model;
            return true;
        }
        catch
        {
        }
        return false;
    }
}

以及用法:

[Route("{id}/items")]
public Item GetItem(string id, [FromUriJson] Filter filter)
{
    //logic
}  

或:

[Route("{id}/items")]
public Item GetItem(string id, [FromUriJson("queryStringKey")] Filter filter)
{
    //logic
} 

如果您只想在某些端点中使用 Json 格式的查询字符串参数和/或不想通过插入新的 IModelBinderProvider 实现来弄乱默认的 IServiceCollection 配置,这会很有用。