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); }
问题分析与解决方案
问题根源
BdeoTransportModel<T>中的Properties是私有字段,且未标记[DataMember],RestSharp序列化器无法访问该字段。- 即使字段公开,默认序列化会将
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
相关产品推荐
相关产品推荐

