【问题标题】:Custom Data Type for "HttpGet" Route in Asp.Net Web Api ProjectAsp.Net Web Api 项目中“HttpGet”路由的自定义数据类型
【发布时间】:2022-02-05 19:02:35
【问题描述】:

我尝试在我的 HttpGet 路由中添加一个“自定义数据类型”的变量。

我有这个代码:

[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"

所以 URL 看起来像这样:

GET Users/55b795827572761a08d735ac

将其从“string”解析为“ObjectId”的代码是:

 ObjectId.TryParse(idString, out idObject);

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

对于 Post 或 Put JSON Payload,我已经找到了解决方案。我知道这是不同的东西。但可能有助于理解该场景,或找到该场景的解决方案:

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);
    }
}

【问题讨论】:

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


    【解决方案1】:

    这将从 Uri 对象绑定它:

    public ResponseSchema Get([FromUri]ObjectId idObject)
    

    所以:?param1=something&amp;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;
        }
    }
    

    【讨论】:

    • FromUri 对于我的场景来说是不够的。我需要告诉 ASP.NET 如何将 URL GET /Users/55b795827572761a08d735ac 中给出的参数转换为 ObjectId。这就是我需要 BindModel 的目的吗?
    • @Matthias 对于无法用FromUriFromBody 推断的更复杂的映射,活页夹是保证事情到达您需要的地方的方法。
    • 所以“绑定器”可以用于我的 URL,就像 JSONConverter 用于我的 JSON 有效负载一样。我会尝试实现 BindModel。
    • 我还没有机会检查它,但我现在接受并投票。
    【解决方案2】:

    如果您有 /api/users?id={{idHere}} 之类的路线,我相信 NikoliaDante 的答案有效。但是,如果您希望拥有更多 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!
    }
    

    【讨论】:

      【解决方案3】:

      ASP.NET Web API 提供了几种方法来做到这一点。看一看 Parameter Binding in Web API 文档。

      总结:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-29
        • 2015-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-23
        • 1970-01-01
        相关资源
        最近更新 更多