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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 15:21:02