【问题标题】:How to bind Json Query string in asp.net core web api如何在 asp.net core web api 中绑定 Json Query 字符串
【发布时间】:2017-11-20 08:03:24
【问题描述】:

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 core web API,我尝试使用以下,但没有成功。

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

【问题讨论】:

  • 看起来像非功能代码。 参数 被称为query 而不是deviceNamedeviceName 是一些类似 json 的查询参数的属性。对于获取,您应该只使用查询参数,并且对于请求正文中的数据进行后期传输。如果您真的希望这种非标准方式(根本不依赖于 ASP.NET Core)仍然有效,您需要编写自己的模型绑定器
  • 在自己与这个问题搏斗了一段时间后,我最终意识到我需要做一个 POST 而不是 GET,即使我想要一个值。正在发送的数据虽然没有持久化,但仍然影响 API 的状态。您需要考虑 RESTful 接口的设计——这是关键。
  • 我想tseng的意思是把它改成Get([FromQuery] string query)

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


【解决方案1】:

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

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

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

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

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 服务的位置,并将模型绑定器插入到列表的前面。这个很重要。如果我们只是将我们的模型绑定器添加到列表中,另一个模型绑定器可能会认为应该使用它(先到先得),因此我们可能永远无法将其添加到我们的列表中。所以一定要在开头插入。

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

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

[HttpGet]
public void Get(MyCustomModel model)
{

}

延伸阅读:

【讨论】:

  • 谢谢。它真的很有帮助。如果我在 api 控制器中有两个 Get 方法,一个获取所有设备,另一个 Get 方法获取设备名称。我在使用相同的 Get 方法时遇到错误。
  • 如果它们都接受相同的有效载荷(整个 JSON 对象),那么您应该只需要一个吧?
  • 现在的缺点是对于您创建的每个自定义模型绑定器,您必须确保您的 json 也可以反序列化它,使用 json 值提供程序可能是一个更好的主意,并且之后启动默认模型 vinder,这样您还可以获得有关各个属性的错误信息
  • 哇。避免直接在控制器中调用 JSON 反序列化器需要做很多工作。
【解决方案2】:

我提出这个问题是因为我有类似的问题。 在我的场景中,实现 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 配置,这可能会很有用。

【讨论】:

  • 您是否必须在任何地方(例如在提供程序中)注册 ModelBinder 或 Attribute?因为在我的情况下,ModelBinder 没有被激活
  • @BoukeVersteegh 不,我只在需要时在控制器方法中使用该属性。 ModelBinderAttribute 将 BinderType 设置为 JsonBinder。在我的例子中,BindModel() 在控制器动作之前被调用。
猜你喜欢
  • 2018-05-23
  • 1970-01-01
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2014-02-02
相关资源
最近更新 更多