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

API返回JSON被外层'd'字段包裹问题排查求助

问题描述

处理API请求时,将DataTable序列化为JSON返回,但响应被外层{'d': '...'}结构包裹,且内部JSON字符串存在转义。

API输入示例

{
    "region_id": "ID-10",
    "document_number": "10-80/81-BIN-00005",
    "industry_no": "10-CNO-0001",
    "posting_date": "14-07-2080",
    "customer_number": "10-CNO-0001",
    "payment_for": [
      {"BillType": "पानी महशुल"}
 ] }

当前API响应示例

{  
    "d": "[{\"CustomerNo\":\"10-CNO-0001\",\"PostingDate\":\"2023-10-31T00:00:00\",\"DocumentNo\":\"10-80/81-BIN-00005\",\"BillType\":\"5\",\"Description\":\"पानी महशुल\",\"Quantity\":0.00000,\"Rate\":0.00000,\"Amount\":1000.00,\"RegionID\":\"ID-10\",\"ExpenseID\":\"0\",\"ActivityID\":\"0\",\"SubActivityID\":\"0\",\"Dimension5ID\":\"\",\"FineAmount\":0.00,\"DiscountAmount\":-40.00,\"IsPaid\":false,\"priority\":\"2\",\"billType\":\"5\"}]"  
}

处理代码

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
[AllowAnonymous]
public string InitialFetch(string region_id, string document_number, string industry_no, string posting_date, string customer_number, List<PaymentForItem> payment_for)
{
    try
    {
        // Create a dictionary to represent the processed data
        var extractedData = new Dictionary<string, object>
        {
            { "region_id", region_id },
            { "document_number", document_number },
            { "industry_no", industry_no },
            { "posting_date", posting_date },
            { "customer_number", customer_number },
            { "payment_for", payment_for.Select(item => new Dictionary<string, object> { { "BillType", item.BillType } }).ToList() }
        };

        // Call the original GetReceivableByBillTypeFromLaravel method a datatable is extracted 
        DataTable receiptData = GetReceivableByBillTypeFromLaravel(extractedData);

        // Serialize the response to JSON
        string jsonResponse = JsonConvert.SerializeObject(receiptData);

        return jsonResponse;
    }
    catch (Exception ex)
    {
        // Handle any exceptions and return an error response
        return JsonConvert.SerializeObject(new { error = "Error: " + ex.Message });
    }
}
问题原因
  1. WebMethod默认包装机制:ASP.NET的WebMethod配合ScriptMethod使用时,会自动将返回值包裹在d属性中,这是ASP.NET AJAX的默认行为,用于兼容早期AJAX请求的安全和跨域处理逻辑。
  2. 双重序列化问题:你在代码中手动用JsonConvert.SerializeObject把DataTable序列化为JSON字符串,WebMethod会将这个字符串当作普通字符串再次进行JSON序列化,导致最终响应里d的值是转义后的JSON字符串,而非直接的JSON对象/数组。
解决方案

方案1:直接返回DataTable(推荐)

修改方法返回类型为DataTable,移除手动序列化步骤,让WebMethod自动处理序列化逻辑:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
[AllowAnonymous]
public DataTable InitialFetch(string region_id, string document_number, string industry_no, string posting_date, string customer_number, List<PaymentForItem> payment_for)
{
    try
    {
        var extractedData = new Dictionary<string, object>
        {
            { "region_id", region_id },
            { "document_number", document_number },
            { "industry_no", industry_no },
            { "posting_date", posting_date },
            { "customer_number", customer_number },
            { "payment_for", payment_for.Select(item => new Dictionary<string, object> { { "BillType", item.BillType } }).ToList() }
        };

        DataTable receiptData = GetReceivableByBillTypeFromLaravel(extractedData);
        return receiptData;
    }
    catch (Exception ex)
    {
        throw new Exception("Error: " + ex.Message);
    }
}

此时WebMethod会自动将DataTable序列化为JSON数组并包裹在d中,内部不会出现转义问题。

方案2:禁用全局响应包装(谨慎使用)

如果需要完全移除外层d结构,可以修改Web.config配置,禁用ASP.NET AJAX的自动包装:

<configuration>
  <system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization enableJsonp="true" maxJsonLength="50000000" />
        <authenticationService enabled="true" requireSSL="false"/>
        <webServices>
          <jsonSerialization serializeAsJson="true"/>
        </webServices>
      </webServices>
    </scripting>
  </system.web.extensions>
</configuration>

注意:该配置会影响所有WebMethod的响应格式,需评估后使用。

方案3:返回自定义对象/匿名对象

若需要自定义响应结构(比如统一错误格式),可以返回匿名对象或强类型对象,避免手动序列化:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
[AllowAnonymous]
public object InitialFetch(string region_id, string document_number, string industry_no, string posting_date, string customer_number, List<PaymentForItem> payment_for)
{
    try
    {
        var extractedData = new Dictionary<string, object>
        {
            { "region_id", region_id },
            { "document_number", document_number },
            { "industry_no", industry_no },
            { "posting_date", posting_date },
            { "customer_number", customer_number },
            { "payment_for", payment_for.Select(item => new Dictionary<string, object> { { "BillType", item.BillType } }).ToList() }
        };

        DataTable receiptData = GetReceivableByBillTypeFromLaravel(extractedData);
        // 将DataTable转换为强类型对象或匿名对象,优化序列化效果
        var result = receiptData.AsEnumerable().Select(row => new
        {
            CustomerNo = row["CustomerNo"],
            PostingDate = row["PostingDate"],
            DocumentNo = row["DocumentNo"],
            BillType = row["BillType"],
            Description = row["Description"],
            Amount = row["Amount"]
            // 其他字段按需添加
        }).ToList();
        return result;
    }
    catch (Exception ex)
    {
        return new { error = "Error: " + ex.Message };
    }
}

这种方式既避免了双重序列化,又能灵活控制响应结构,WebMethod会自动完成序列化。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:36:00