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

使用HttpClient调用SharePoint REST API创建文件夹返回400错误求助

SharePoint REST 创建文件夹:HttpWebRequest 成功但 HttpClient 返回 Bad Request 排查

我通过 HttpWebRequest 能成功在 SharePoint 文档库中创建文件夹,但改用 HttpClient 发起相同请求时,POST、PUT、UPDATE 均返回「Bad Request」错误,GET 请求可正常执行。以下是相关代码及问题排查:

可正常工作的 HttpWebRequest 代码

AddFolderAsync 方法

public async Task<string> AddFolderAsync(string library, string folderName)
{
      var _serverRelativeUrl = string.Join("/", new string[] { library, folderName });
      var _type = "SP.Folder";
      var kvPropertyValues = new List<KeyValuePair<string, string>>
      {
          new KeyValuePair<string, string>(ServerRelativeUrl, _serverRelativeUrl)
      };

      string json = GetJsonMetadataStringByType(_type, kvPropertyValues);
      // 返回格式: '{"__metadata": {"type": "SP.Folder"},"ServerRelativeUrl":"TEST/<folderName>"}'
      const string urlTemplate = "{0}/_api/web/folders";
      var restUrl = string.Format(urlTemplate, webUrl);
                                        
      var endpoint = _authenticationManager.GetWebRequest(restUrl, HttpMethod.Post, null, false);

      using Stream requestStream = endpoint.GetRequestStream();
      using StreamWriter writer = new(requestStream);
      {
            writer.Write(json);
            writer.Flush();
      }
            
       HttpWebResponse httpResponse = await GetResponse(endpoint);

       // 后续处理逻辑...
}

GetWebRequest 方法

public HttpWebRequest GetWebRequest(string url, HttpMethod method, string xHttpMethod, bool noMetaData)
{
      try
      {
            var accessToken = EnsureAccessTokenAsync(siteURI, user, password).GetAwaiter().GetResult();
            var odata = noMetaData ? OData.NOMETADATA : OData.VERBOSE;
            var endpointRequest = HttpWebRequest.CreateHttp(url);
            endpointRequest.Method = method.ToString();

            endpointRequest.Accept = $"application/json;odata={odata}";
            endpointRequest.ContentType = $"application/json;odata={odata}";
            endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);

            if (!string.IsNullOrEmpty(xHttpMethod))
            {
                  endpointRequest.Headers.Add("IF-MATCH", "*");
                  endpointRequest.Headers.Add("X-HTTP-Method", xHttpMethod.ToUpper());
            }

            return endpointRequest;
       }
       catch (System.Exception)
       {
            throw;
       }
}

返回 Bad Request 的 HttpClient 代码

调用代码

var sc = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
var _httpResponse = await _authenticationManager.GetHttpResponseFromRequestWithBody(restUrl, HttpMethod.Post, sc, null);

GetHttpRequestMessage 和 GetHttpResponseFromRequestWithBody 方法

private HttpRequestMessage GetHttpRequestMessage(string url, HttpMethod method, string xHttpMethod, string odata)
{
    var request = new HttpRequestMessage(method, url);

    // 设置 Bearer Token
    var accessToken = EnsureAccessTokenAsync(siteURI, user, password).GetAwaiter().GetResult();
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

    // 设置 Accept 头
    var acceptHeader = MediaTypeWithQualityHeaderValue.Parse($"application/json;odata={odata}");
    request.Headers.Accept.Add(acceptHeader);

    // IF-Match 头处理
    if (!string.IsNullOrEmpty(xHttpMethod))
    {
        // request.Headers.Add("IF-MATCH", "*");
        request.Headers.IfMatch.Add(new EntityTagHeaderValue("*"));
        request.Headers.Add("X-HTTP-Method", xHttpMethod.ToUpper());
    }

    return request;
}

public Task<HttpResponseMessage> GetHttpResponseFromRequestWithBody(string url, HttpMethod method, HttpContent content, string xHttpMethod, string odata = OData.NOMETADATA)
{
    var request = GetHttpRequestMessage(url, method, xHttpMethod, odata);
    request.Content = content;
    Debug.WriteLine(request.Content);

    return httpClient.SendAsync(request);
}

问题原因及解决方案

1. OData 元数据模式不一致

HttpWebRequest 调用时传入的 noMetaData 为 false,因此使用的是 OData.VERBOSE 模式;但 HttpClient 的 GetHttpResponseFromRequestWithBody 方法默认参数是 OData.NOMETADATA,导致请求头的 Accept 与 HttpWebRequest 不一致。

解决: 调用时显式指定 odata 参数为 OData.VERBOSE:

var _httpResponse = await _authenticationManager.GetHttpResponseFromRequestWithBody(restUrl, HttpMethod.Post, sc, null, OData.VERBOSE);

2. StringContent 的 ContentType 缺少 OData 参数

HttpWebRequest 中设置的 ContentType 是 application/json;odata=verbose,但 HttpClient 的 StringContent 只设置了 application/json,缺少 odata=verbose 后缀,这会导致 SharePoint REST 无法正确解析请求体。

解决: 创建 StringContent 时指定完整的 ContentType:

var sc = new StringContent(json, Encoding.UTF8, "application/json;odata=verbose");

3. 异步方法同步调用的潜在问题

EnsureAccessTokenAsync 是异步方法,但在两个实现中都使用 GetAwaiter().GetResult() 同步获取结果,这可能导致死锁或上下文问题(虽然 GET 请求正常,但 POST 场景可能触发)。

建议: 将相关方法修改为异步,使用 await 获取 Token:

// 修改 GetHttpRequestMessage 为异步方法
private async Task<HttpRequestMessage> GetHttpRequestMessageAsync(string url, HttpMethod method, string xHttpMethod, string odata)
{
    var request = new HttpRequestMessage(method, url);
    var accessToken = await EnsureAccessTokenAsync(siteURI, user, password);
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    // 其余逻辑不变...
}

// 同步修改 GetHttpResponseFromRequestWithBody
public async Task<HttpResponseMessage> GetHttpResponseFromRequestWithBodyAsync(string url, HttpMethod method, HttpContent content, string xHttpMethod, string odata = OData.NOMETADATA)
{
    var request = await GetHttpRequestMessageAsync(url, method, xHttpMethod, odata);
    request.Content = content;
    return await httpClient.SendAsync(request);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:05:59