Web API 项目中“HttpGet”路由 Asp.Net 自定义数据类型

本文关键字:Asp 路由 Net 定义数据类型 HttpGet API 项目 Web | 更新日期: 2023-09-27 18:31:44

我尝试在我的HttpGet Route中添加一个"自定义数据类型"的变量。

我有这个代码:

[HttpGet("{idObject}")]
public ResponseSchema Get(ObjectId idObject)
{
    if (idObject == null) {
        throw new BodyParseException();
    }
    var user = _usersLogic.GetById(idObject);
    if (user == null) {
        _response.Success = false;
        _response.ErrorCode = "UserDoesNotExist";
    }
    else {
        _response.Objects.Add(user);
    }
    return _response;
}

ObjectId 是使用 MongoDB.Bson 定义的数据类型。

对于 Json 序列化和反序列化,我们已经有了在两端自动转换的代码。但是这可以在 URL 本身中类似地完成吗?

我们现在正在使用这个 Mvc 版本:

"Microsoft.AspNet.Mvc": "6.0.0-beta8"

所以网址看起来像这样:

GET Users/55b795827572761a08d735ac

将其从"字符串"解析为"ObjectId"的代码是:

 ObjectId.TryParse(idString, out idObject);

问题是将TryParse代码放在哪里。因为我需要告诉 ASP.NET 它应该如何将idObject从String解析为ObjectId。由于URL基本上是一个字符串。

对于发布或放置 JSON 有效负载,我已经找到了解决方案。我知道这是不同的东西。但是,了解该场景或找到此方案的解决方案可能会有所帮助:

public class EntityBaseDocument
{
    [JsonConverter(typeof(ObjectIdConverter))]
    public ObjectId Id { get; set; }
}
// Since we have this value converter. We can use ObjectId everywhere
public class ObjectIdConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value.ToString());
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        return new ObjectId(token.ToObject<string>());
    }
    public override bool CanConvert(Type objectType)
    {
        return typeof(ObjectId).IsAssignableFrom(objectType);
    }
}

Web API 项目中“HttpGet”路由 Asp.Net 自定义数据类型

这会

从 Uri 对象绑定它:

public ResponseSchema Get([FromUri]ObjectId idObject)

所以:?param1=something&param2=sometingelse

这将从主体(例如 JSon 对象)绑定它

public ResponseSchema Get([FromBody]ObjectId idObject)

或者你可以自己滚动:

public ResponseSchema Get([ModelBinder(typeof(MyObjectBinder))]ObjectId idObject)

模型绑定程序 asp.net 的示例如下:

public class GeoPointModelBinder : IModelBinder
{
    // List of known locations.
    private static ConcurrentDictionary<string, GeoPoint> _locations
        = new ConcurrentDictionary<string, GeoPoint>(StringComparer.OrdinalIgnoreCase);
    static GeoPointModelBinder()
    {
        _locations["redmond"] = new GeoPoint() { Latitude = 47.67856, Longitude = -122.131 };
        _locations["paris"] = new GeoPoint() { Latitude = 48.856930, Longitude = 2.3412 };
        _locations["tokyo"] = new GeoPoint() { Latitude = 35.683208, Longitude = 139.80894 };
    }
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(GeoPoint))
        {
            return false;
        }
        ValueProviderResult val = bindingContext.ValueProvider.GetValue(
            bindingContext.ModelName);
        if (val == null)
        {
            return false;
        }
        string key = val.RawValue as string;
        if (key == null)
        {
            bindingContext.ModelState.AddModelError(
                bindingContext.ModelName, "Wrong value type");
            return false;
        }
        GeoPoint result;
        if (_locations.TryGetValue(key, out result) || GeoPoint.TryParse(key, out result))
        {
            bindingContext.Model = result;
            return true;
        }
        bindingContext.ModelState.AddModelError(
            bindingContext.ModelName, "Cannot convert value to Location");
        return false;
    }
}

我相信NikoliaDante的答案如果你有像/api/users?id={{idHere}}这样的路线,就会起作用。但是,如果您希望拥有更多的 RESTful 路线,下面的解决方案将为您解决问题。我刚刚在 Web API 2 应用程序中对此进行了测试,它运行良好。这将处理您可能有路由的用例,例如/api/users/{{userId}}/something/{{somethingId}}。

//Http Parameter Binding Magic
public class ObjectIdParameterBinding : HttpParameterBinding
{
    public ObjectIdParameterBinding(HttpParameterDescriptor p) : base(p){ }
    public override Task ExecuteBindingAsync(System.Web.Http.Metadata.ModelMetadataProvider metadataProvider, HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
    {
        var value = actionContext.ControllerContext.RouteData.Values[Descriptor.ParameterName].ToString();
        SetValue(actionContext, ObjectId.Parse(value));
        var tsc = new TaskCompletionSource<object>();
        tsc.SetResult(null);
        return tsc.Task;
    }
}
//Binding Attribute
public class ObjectIdRouteBinderAttribute : ParameterBindingAttribute
{
    public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
    {
        return new ObjectIdParameterBinding(parameter);
    }
}
//Controller Example
[Route("api/users/{id}")]
public async Task<User> Get([ObjectIdRouteBinder] ObjectId id) 
{
    //Yay!
}

ASP.NET Web API 提供了几种方法来执行此操作。看看Web API 文档中的参数绑定。

总结:

  • FromUriAttribute - 用于简单的 DTO 类
  • TypeConverter - 帮助 Web API 将类视为简单类型
  • HttpParameterBinding - 允许创建行为属性
  • ValueProvider - 适用于更复杂的情况
  • IActionValueBinder - 编写自己的参数绑定过程