.NET Web API接收Post XML请求时参数为NULL的问题排查
.NET Web API本地测试时XML请求体无法绑定到参数(生产环境正常)
问题现象
刚接触.NET技术栈,协助修改生产环境运行的.NET Web API代码,本地测试时遇到异常:API仅通过Postman发送带XML请求体的POST请求测试,服务器能接收到完整请求体,但接口方法中的SummarySearchRequestType类型参数req始终为NULL。生产环境运行正常,仅本地出现该问题。
相关代码与请求信息
接口方法
[Route("summary")] [ResponseType(typeof(SummarySearchResponseType))] public IHttpActionResult PostSummary([FromBody] SummarySearchRequestType req) { try { var response = _searchService.GetSummary(req); if (response == null) { return BadRequest(); } return Ok(response); } catch { return StatusCode(System.Net.HttpStatusCode.InternalServerError); } }
SearchService核心方法
public SummarySearchResponseType GetSummary(SummarySearchRequestType request) { Serilog.Log.Information("GetSummary request = {0}", request); // Validate required fields in request if (request == null) { Serilog.Log.Logger.Warning("One or more required search fields are missing"); return null; } if (string.IsNullOrEmpty(request.ClientNumber)) { Serilog.Log.Logger.Warning("Missing required search field 'ClientNumber'"); return null; } if (request.LossDate == null) { Serilog.Log.Logger.Warning("Missing required search field 'LossDate'"); return null; } var response = new SummarySearchResponseType { RequestID = request.RequestID }; try { using (IMultipleResults sprocResults = _dataContext.UspGetDetailedResponse( request.ClientNumber, request.PolicyNumber, request.PolicySuffix, request.ClientAccountNum, request.LossDate.ToShortDateString(), request.PolicyType, request.LineCode, request.AgencyCode, request.Searchtype, request.Lob, request.InsuredSearch?.FirstName, request.InsuredSearch?.LastName, request.InsuredSearch?.FullName, request.InsuredSearch?.DBAName, request.InsuredSearch?.Address.Address1, request.InsuredSearch?.Address.Address2, request.InsuredSearch?.Address.City, request.InsuredSearch?.Address.State, request.InsuredSearch?.Address.PostalCode, request.InsuredSearch?.Address.County, request.InsuredSearch?.Address.Country, request.InsuredSearch?.ClientAccountnumber, request.InsuredSearch?.Email, request.InsuredSearch?.Phone, request.InsuredSearch?.Comment, request.RiskUnitSearch?.RiskUnitID, request.RiskUnitSearch?.RiskUnitName, request.RiskUnitSearch?.LocationSearch?.BuildingID, request.RiskUnitSearch?.LocationSearch?.Address?.Address1, request.RiskUnitSearch?.LocationSearch?.Address?.Address2, request.RiskUnitSearch?.LocationSearch?.Address?.City, request.RiskUnitSearch?.LocationSearch?.Address?.State, request.RiskUnitSearch?.LocationSearch?.Address?.PostalCode, request.RiskUnitSearch?.LocationSearch?.Address?.County, request.RiskUnitSearch?.LocationSearch?.Address?.Country, request.RiskUnitSearch?.VehicleSearch.VIN, request.RiskUnitSearch?.VehicleSearch.Year, request.RiskUnitSearch?.VehicleSearch.Make)) { // Policies var result1 = sprocResults.GetResult<UspGetDetailedResponseResult1>(); var policies = _mapper.Map<IEnumerable<PolicyType>>(result1).ToArray(); // Insured var result2 = sprocResults.GetResult<UspGetDetailedResponseResult2>(); var insured = _mapper.Map<IEnumerable<ParticipantType>>(result2).ToArray(); //Risk Unit var result3 = sprocResults.GetResult<UspGetDetailedResponseResult3>(); var riskUnits = _mapper.Map<IEnumerable<RiskUnitType>>(result3).ToArray(); // Attach Participants and RiskUnits to Policies via PolicyNumber policies.ToList().ForEach(p => { p.Participants = insured.Where(i => i.PolicyNumber == p.PolicyNumber).ToArray(); p.RiskUnits = riskUnits.Where(i => i.PolicyNumber == p.PolicyNumber).ToArray(); }); response.Policies = policies; response.PolicyCnt = policies.Length.ToString(); Serilog.Log.Information("GetSummary response = {0}", response); return response; } } catch (Exception ex) { Serilog.Log.Error(ex, $"{ex}", ex); throw ex; } }
SummarySearchRequestType定义
public partial class SummarySearchRequestType { public override string ToString() { XmlSerializer responseSerializer = new XmlSerializer(this.GetType()); using (StringWriter responseWriter = new StringWriter()) { responseSerializer.Serialize(responseWriter, this); var xmlResponse = responseWriter.ToString(); return xmlResponse; } } }
public partial class SummarySearchRequestType { private string requestIDField; private string clientNumberField; private string policyNumberField; private string policySuffixField; private string clientAccountNumField; private System.DateTime lossDateField; private bool lossDateFieldSpecified; private string policyTypeField; private string lineCodeField; private InsuredSearchType insuredSearchField; private RiskUnitSearchType riskUnitSearchField; private string agencyCodeField; private string searchtypeField; private string lobField; /// <remarks/> public string RequestID { get { return this.requestIDField; } set { this.requestIDField = value; } } /// <remarks/> public string ClientNumber { get { return this.clientNumberField; } set { this.clientNumberField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string PolicyNumber { get { return this.policyNumberField; } set { this.policyNumberField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string PolicySuffix { get { return this.policySuffixField; } set { this.policySuffixField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string ClientAccountNum { get { return this.clientAccountNumField; } set { this.clientAccountNumField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(DataType="date")] public System.DateTime LossDate { get { return this.lossDateField; } set { this.lossDateField = value; } } /// <remarks/> [System.Xml.Serialization.XmlIgnoreAttribute()] public bool LossDateSpecified { get { return this.lossDateFieldSpecified; } set { this.lossDateFieldSpecified = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string PolicyType { get { return this.policyTypeField; } set { this.policyTypeField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string LineCode { get { return this.lineCodeField; } set { this.lineCodeField = value; } } /// <remarks/> public InsuredSearchType InsuredSearch { get { return this.insuredSearchField; } set { this.insuredSearchField = value; } } /// <remarks/> public RiskUnitSearchType RiskUnitSearch { get { return this.riskUnitSearchField; } set { this.riskUnitSearchField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string AgencyCode { get { return this.agencyCodeField; } set { this.agencyCodeField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string Searchtype { get { return this.searchtypeField; } set { this.searchtypeField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Namespace="http://sedgwickcms.com/claims/entities/Policy/v1310")] public string Lob { get { return this.lobField; } set { this.lobField = value; } } }
Postman发送的XML请求体
<?xml version="1.0" encoding="utf-8" ?><SummarySearchRequest xmlns="***"> <RequestID>***</RequestID> <ClientNumber>***</ClientNumber> <LossDate>***</LossDate> <PolicyNumber xmlns="***">***</PolicyNumber> </SummarySearchRequest>
(注:敏感数据已替换为***)
运行环境
服务运行于Windows11虚拟机的Visual Studio IIS Express(HTTPS端口44375)
排查方向
- XML命名空间不匹配:请求根节点
SummarySearchRequest的xmlns值需与SummarySearchRequestType类定义的默认命名空间一致(检查类是否有[XmlRoot]特性指定命名空间,若生产环境的请求命名空间正确,本地需保持一致)。 - 请求头Content-Type设置错误:Postman需设置
Content-Type: application/xml,若误设为application/json或其他类型,Web API会无法解析XML请求体。 - XML序列化配置缺失:确认Web API项目的
WebApiConfig.cs中是否启用了XML格式化器,生产环境可能已配置但本地调试时未正确加载:config.Formatters.XmlFormatter.UseXmlSerializer = true; - LossDate字段格式问题:
LossDate属性标注了DataType="date",需确保请求中的日期格式符合XML date类型标准(如yyyy-MM-dd),若格式错误会导致序列化失败,参数绑定为NULL。 - IIS Express权限或配置差异:本地IIS Express可能存在与生产环境IIS的配置差异,可尝试清理项目缓存、重启IIS Express,或直接部署到本地IIS测试。
内容的提问来源于stack exchange,提问作者James_Griffiths
相关产品推荐
相关产品推荐

