You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RestSharp无法序列化实体/对象,POST请求缺失必填字段

问题:RestSharp 108.0.3序列化自定义传输模型失败导致POST请求参数缺失

使用RestSharp Client(版本108.0.3)发送POST请求时收到错误:Bad Request: Some of mandatory fields are missing,排查后确认是请求参数未正确序列化。直接传递UserModel实体时请求正常,但需要通过自定义传输模型做数据转换(如密码格式处理),因此必须解决传输模型的序列化问题。


原始UserModel定义

[DataContract]
public class UserModel : IBdeoModel
{
    [Required]
    //[property: JsonPropertyName("name")]
    [DataMember(Name = "email")]
    public string ClientEmail { get; set; }

    [Required]
    //[property: JsonPropertyName("password")]
    [DataMember(Name = "password")]
    public string ClientSecrent { get; set; }

    [IgnoreDataMember]
    public bool IsNameValue => true;
}

API调用代码

public class BdeoLoginAPI : ApiRoutable, IBdeoLoginApi
{
    private readonly IClient _client;
    public override string MidRoute => "/prod/v2/login";

    public BdeoLoginAPI(IClient client)
    {
        _client = client;
    }

    /// <summary>
    /// Authenticates the request to Bdeo
    /// </summary>
    /// <param cref="UserModel">User Auth Model</param>
    /// <returns>The result set of properties created in hubspot.</returns>
    public UserTokenModel<LoginAuthDetails> AuthenticateBdeo(UserModel entity)
    {
        string path = GetRoute<UserModel>();
        BdeoTransportObject<UserModel> transportModel = new BdeoTransportObject<UserModel>(entity);
        //return _client.Execute<UserTokenModel<LoginAuthDetails>, UserModel>(path, entity, Method.Post);
        return _client.Execute<UserTokenModel<LoginAuthDetails>, BdeoTransportObject<UserModel>>(path, transportModel, Method.Post);
    }
}

自定义传输模型代码

[DataContract]
public class BdeoTransportObject<T> : BdeoTransportModel<T>
{
    public BdeoTransportObject() { }

    public BdeoTransportObject(T model)
    {
        ToPropertyTransportModel(model);
    }
}

[DataContract]
public abstract class BdeoTransportModel<T>
{
    private Dictionary<string, object> Properties { get; set; }
  
    internal void ToPropertyTransportModel(T model)
    {
        var propertiesToAdd = new Dictionary<string, object>();

        PropertyInfo[] modelProperties = model.GetType().GetProperties();

        foreach (PropertyInfo modelProp in modelProperties)
        {
            var memberAttrib = modelProp.GetCustomAttribute(typeof(DataMemberAttribute)) as DataMemberAttribute;
            object modelValue = modelProp.GetValue(model);

            if (modelValue == null || memberAttrib == null || modelValue.ToString() == string.Empty)
            {
                continue;
            }

            //some other logic

            propertiesToAdd.Add(memberAttrib.Name, modelValue);
        }

        Properties = propertiesToAdd;
    }
}

RestRequest执行代码

public T Execute<T, K>(string absoluteUriPath, K entity, Method method = Method.Get) where T : new()
    => SendReceiveRequest<T, K>(absoluteUriPath, method, entity);

private RestRequest ConfigureRequestAuthentication(string path, Method method)
{
    string fullPath = $"{BasePath.TrimEnd('/')}/{path.Trim('/')}";
    RestRequest request = new RestRequest(fullPath, method);
    request.RequestFormat = DataFormat.Json;
    
    return request;
}

private T SendReceiveRequest<T, K>(string path, Method method, K entity) where T : new()
{
    RestRequest request = ConfigureRequestAuthentication(path, method);

    if (!entity.Equals(default(K)))
    {
        //string json = System.Text.Json.JsonSerializer.Serialize(entity);
        //string json = JsonConvert.SerializeObject(entity);
        //request.AddJsonBody(entity);
        //string content = JsonConvert.SerializeObject(entity, Formatting.Indented);
        request.AddBody(entity);
        //request.AddParameter("text/json", entity, ParameterType.RequestBody);
    }

    var response = _client.Execute<T>(request);

    if (!response.IsSuccessful)
    {
        throw new InvalidOperationException("Failed with " + response.StatusCode.ToString() + ". Error Message: " + response.ErrorMessage);
    }

    return response.Data;
}

验证情况

直接传递字段名与接口一致的UserModel时请求正常:

[DataContract]
public class UserModel : IBdeoModel
{
    [Required]
    public string email { get; set; }

    [Required]
    public string password { get; set; }

    [IgnoreDataMember]
    public bool IsNameValue => true;
}

public UserTokenModel<LoginAuthDetails> AuthenticateBdeo(UserModel entity)
{
    string path = GetRoute<UserModel>();
    BdeoTransportObject<UserModel> transportModel = new BdeoTransportObject<UserModel>(entity);
    return _client.Execute<UserTokenModel<LoginAuthDetails>, UserModel>(path, entity, Method.Post);
   // return _client.Execute<UserTokenModel<LoginAuthDetails>, BdeoTransportObject<UserModel>>(path, transportModel, Method.Post);
}

问题分析与解决方案

问题根源

  1. BdeoTransportModel<T>中的Properties是私有字段,且未标记[DataMember],RestSharp序列化器无法访问该字段。
  2. 即使字段公开,默认序列化会将Properties序列化为嵌套对象(如{"Properties": {"email": "...", "password": "..."}}),但接口期望平级的email和password字段。

解决方案1:让传输模型继承字典接口

修改抽象类使其实现IDictionary<string, object>,RestSharp会直接将键值对序列化为平级JSON字段:

[DataContract]
public abstract class BdeoTransportModel<T> : Dictionary<string, object>
{
    internal void ToPropertyTransportModel(T model)
    {
        PropertyInfo[] modelProperties = model.GetType().GetProperties();

        foreach (PropertyInfo modelProp in modelProperties)
        {
            var memberAttrib = modelProp.GetCustomAttribute(typeof(DataMemberAttribute)) as DataMemberAttribute;
            object modelValue = modelProp.GetValue(model);

            if (modelValue == null || memberAttrib == null || modelValue.ToString() == string.Empty)
            {
                continue;
            }

            // 此处添加密码处理等自定义逻辑
            this.Add(memberAttrib.Name, modelValue);
        }
    }
}

解决方案2:手动序列化字典为JSON字符串

在SendReceiveRequest中手动序列化Properties字典,避免RestSharp自动序列化的问题:

private T SendReceiveRequest<T, K>(string path, Method method, K entity) where T : new()
{
    RestRequest request = ConfigureRequestAuthentication(path, method);

    if (!entity.Equals(default(K)))
    {
        if (entity is BdeoTransportModel<UserModel> transportModel)
        {
            // 先将Properties改为公开字段,或添加GetProperties()方法
            string json = JsonConvert.SerializeObject(transportModel.Properties);
            request.AddStringBody(json, DataFormat.Json);
        }
        else
        {
            request.AddBody(entity);
        }
    }

    var response = _client.Execute<T>(request);

    if (!response.IsSuccessful)
    {
        throw new InvalidOperationException("Failed with " + response.StatusCode.ToString() + ". Error Message: " + response.ErrorMessage);
    }

    return response.Data;
}

解决方案3:直接传递字典参数

若Properties字段已公开,可直接将字典作为请求体参数:

if (entity is BdeoTransportObject<UserModel> transportModel)
{
    request.AddParameter("application/json", transportModel.Properties, ParameterType.RequestBody);
}

内容的提问来源于stack exchange,提问作者Kerieks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 18:26:03