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

SharePoint订阅版REST API添加列表项失败(403禁止访问)

解决SharePoint REST API添加列表项时的403 Forbidden错误

问题场景

在SharePoint Subscription Edition环境中,针对名为test2的列表,通过C#代码调用REST API添加列表项时,代码可成功获取Form Digest和列表实体类型,但执行POST请求添加项时返回403 Forbidden错误。使用的farmadmin账户可通过站点网页正常添加列表项。

原代码

using Newtonsoft.Json;
using System.Net;
using System.Net.Http.Headers;
using System.Security;
using System.Text;

namespace TestList
{
    public partial class Form1 : System.Windows.Forms.Form
    {
        private static readonly string siteUrl = "http://serverhost:39629/";
        private static readonly string listName = "test2";
        private static readonly string username = "farmadmin";
        private static readonly string password = "Password";
        private static readonly string domain = "DOMAIN.LOCAL";

        private readonly CookieContainer cookieContainer = new CookieContainer();

        public Form1()
        {
            InitializeComponent();
            // 仅测试/开发环境绕过SSL验证
            ServicePointManager.ServerCertificateValidationCallback =
                (sender, certificate, chain, sslPolicyErrors) => true;

            // 启用TLS协议
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
        }

        private async void btnAdd_Click(object sender, EventArgs e)
        {
            string newItemTitle = $"Test Item from C# REST - {DateTime.Now:yyyyMMdd_HHmmss}";
            MessageBox.Show($"Attempting to add item: {newItemTitle}", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

            var securePassword = new SecureString();
            foreach (char c in password) securePassword.AppendChar(c);
            securePassword.MakeReadOnly();

            var credentials = new NetworkCredential(username, securePassword, domain);

            using (var handler = new HttpClientHandler { Credentials = credentials, UseDefaultCredentials = false })
            using (var client = new HttpClient(handler))
            {
                client.DefaultRequestHeaders.Accept.Clear();
                var acceptMediaType = new MediaTypeWithQualityHeaderValue("application/json");
                acceptMediaType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
                client.DefaultRequestHeaders.Accept.Add(acceptMediaType);

                try
                {
                    // 获取Form Digest
                    MessageBox.Show("Retrieving Form Digest Value...", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    var digestUrl = $"{siteUrl}_api/contextinfo";
                    var digestRequestContent = new StringContent(string.Empty);
                    var contentTypeHeaderForDigest = new MediaTypeHeaderValue("application/json");
                    contentTypeHeaderForDigest.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
                    digestRequestContent.Headers.ContentType = contentTypeHeaderForDigest;

                    var digestResponse = await client.PostAsync(digestUrl, digestRequestContent);
                    digestResponse.EnsureSuccessStatusCode();

                    var digestResponseBody = await digestResponse.Content.ReadAsStringAsync();
                    dynamic digestData = JsonConvert.DeserializeObject(digestResponseBody);
                    string formDigestValue = digestData.d.GetContextWebInformation.FormDigestValue;

                    if (string.IsNullOrWhiteSpace(formDigestValue))
                    {
                        throw new Exception("Failed to retrieve Form Digest Value. The response from _api/contextinfo was: " + digestResponseBody);
                    }
                    MessageBox.Show($"Form Digest Value retrieved successfully.", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

                    // 获取列表实体类型
                    MessageBox.Show($"Retrieving List Item Entity Type Name for list '{listName}'...", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    var listSchemaUrl = $"{siteUrl}_api/web/lists/getByTitle('{listName}')?$select=ListItemEntityTypeFullName";
                    var listSchemaResponse = await client.GetAsync(listSchemaUrl);
                    listSchemaResponse.EnsureSuccessStatusCode();

                    var listSchemaResponseBody = await listSchemaResponse.Content.ReadAsStringAsync();
                    dynamic listSchemaData = JsonConvert.DeserializeObject(listSchemaResponseBody);
                    string listItemEntityTypeFullName = listSchemaData.d.ListItemEntityTypeFullName;

                    if (string.IsNullOrWhiteSpace(listItemEntityTypeFullName))
                    {
                        throw new Exception($"Failed to retrieve ListItemEntityTypeFullName for list '{listName}'.");
                    }
                    MessageBox.Show($"ListItemEntityTypeFullName for '{listName}': {listItemEntityTypeFullName}", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

                    // 准备请求体
                    var itemPayload = new
                    {
                        __metadata = new { type = listItemEntityTypeFullName },
                        Title = newItemTitle
                    };
                    var jsonPayload = JsonConvert.SerializeObject(itemPayload);
                    MessageBox.Show($"Prepared Item Payload (JSON body): {jsonPayload}", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

                    // 执行添加请求
                    var addUrl = $"{siteUrl}_api/web/lists/getByTitle('{listName}')/items";
                    var addRequestContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
                    var contentTypeForAddContent = new MediaTypeHeaderValue("application/json");
                    contentTypeForAddContent.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
                    addRequestContent.Headers.ContentType = contentTypeForAddContent;
                    addRequestContent.Headers.Add("X-RequestDigest", formDigestValue);

                    MessageBox.Show($"Attempting to add new item to list '{listName}' via REST API...", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    var addResponse = await client.PostAsync(addUrl, addRequestContent);
                    addResponse.EnsureSuccessStatusCode();

                    MessageBox.Show($"Item '{newItemTitle}' added successfully to list '{listName}'!", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (HttpRequestException httpEx)
                {
                    string errorMessage = $"HTTP Request Error: {httpEx.Message}";
                    if (httpEx.StatusCode.HasValue)
                    {
                        errorMessage += $"\nStatus Code: {(int)httpEx.StatusCode.Value} {httpEx.StatusCode.Value}";
                    }
                    if (httpEx.InnerException != null)
                    {
                        errorMessage += $"\nInner Exception: {httpEx.InnerException.Message}";
                    }

                    HttpResponseMessage responseMessage = null;
                    if (httpEx.Data.Contains("HttpResponseMessage"))
                    {
                        responseMessage = httpEx.Data["HttpResponseMessage"] as HttpResponseMessage;
                    }

                    if (responseMessage != null)
                    {
                        try
                        {
                            string responseBody = await responseMessage.Content.ReadAsStringAsync();
                            errorMessage += $"\nResponse Body: {responseBody}";
                        }
                        catch (Exception contentReadEx)
                        {
                            errorMessage += $"\nCould not read Response Body: {contentReadEx.Message}";
                        }
                    }

                    MessageBox.Show(errorMessage, "SharePoint REST API Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                catch (Exception ex)
                {
                    string errorMessage = $"An unexpected error occurred: {ex.Message}";
                    if (ex.InnerException != null)
                    {
                        errorMessage += $"\nInner Exception: {ex.InnerException.Message}";
                    }
                    MessageBox.Show(errorMessage, "SharePoint REST API Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
    }
}

错误信息

---------------------------
SharePoint REST API Error
---------------------------
HTTP Request Error: Response status code does not indicate success: 403 (FORBIDDEN).
Status Code: 403 Forbidden
---------------------------
OK   
---------------------------

核心问题及修复方案

1. Cookie容器未绑定到请求处理程序

SharePoint的Form Digest令牌与会话Cookie绑定,若获取Digest和添加项的请求使用不同会话,会导致令牌验证失败返回403。原代码中定义了cookieContainer但未赋值给HttpClientHandler,导致两次请求会话不共享。

修复代码:
修改HttpClientHandler的初始化代码,添加Cookie容器绑定和预认证设置:

using (var handler = new HttpClientHandler 
{ 
    Credentials = credentials, 
    UseDefaultCredentials = false,
    CookieContainer = cookieContainer, // 绑定Cookie容器
    PreAuthenticate = true, // 启用NTLM预认证
    AllowAutoRedirect = true // 允许自动跳转
})

2. 简化请求头设置(可选)

可以直接设置Content-Type为application/json;odata=verbose,避免拆分参数的冗余操作:

// 替换原Content-Type设置代码
addRequestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");

3. 验证权限细节(排查用)

  • 确认farmadmin账户拥有站点的编辑权限(网页能操作则已满足,但可检查是否是直接权限而非继承)
  • 检查Web应用程序是否启用了安全验证(默认启用,若禁用可能导致其他问题,但403通常与此无关)
  • 确认站点未启用自定义权限限制(如IRM或列表级权限拦截)

修改后的完整关键代码片段

private async void btnAdd_Click(object sender, EventArgs e)
{
    string newItemTitle = $"Test Item from C# REST - {DateTime.Now:yyyyMMdd_HHmmss}";
    MessageBox.Show($"Attempting to add item: {newItemTitle}", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

    var securePassword = new SecureString();
    foreach (char c in password) securePassword.AppendChar(c);
    securePassword.MakeReadOnly();

    var credentials = new NetworkCredential(username, securePassword, domain);

    // 修复后的HttpClientHandler配置
    using (var handler = new HttpClientHandler 
    { 
        Credentials = credentials, 
        UseDefaultCredentials = false,
        CookieContainer = cookieContainer,
        PreAuthenticate = true,
        AllowAutoRedirect = true
    })
    using (var client = new HttpClient(handler))
    {
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata=verbose"));

        try
        {
            // 获取Form Digest
            MessageBox.Show("Retrieving Form Digest Value...", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
            var digestUrl = $"{siteUrl}_api/contextinfo";
            var digestRequestContent = new StringContent(string.Empty);
            digestRequestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");

            var digestResponse = await client.PostAsync(digestUrl, digestRequestContent);
            digestResponse.EnsureSuccessStatusCode();

            var digestResponseBody = await digestResponse.Content.ReadAsStringAsync();
            dynamic digestData = JsonConvert.DeserializeObject(digestResponseBody);
            string formDigestValue = digestData.d.GetContextWebInformation.FormDigestValue;

            if (string.IsNullOrWhiteSpace(formDigestValue))
            {
                throw new Exception("Failed to retrieve Form Digest Value. The response from _api/contextinfo was: " + digestResponseBody);
            }
            MessageBox.Show($"Form Digest Value retrieved successfully.", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

            // 获取列表实体类型
            MessageBox.Show($"Retrieving List Item Entity Type Name for list '{listName}'...", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
            var listSchemaUrl = $"{siteUrl}_api/web/lists/getByTitle('{listName}')?$select=ListItemEntityTypeFullName";
            var listSchemaResponse = await client.GetAsync(listSchemaUrl);
            listSchemaResponse.EnsureSuccessStatusCode();

            var listSchemaResponseBody = await listSchemaResponse.Content.ReadAsStringAsync();
            dynamic listSchemaData = JsonConvert.DeserializeObject(listSchemaResponseBody);
            string listItemEntityTypeFullName = listSchemaData.d.ListItemEntityTypeFullName;

            if (string.IsNullOrWhiteSpace(listItemEntityTypeFullName))
            {
                throw new Exception($"Failed to retrieve ListItemEntityTypeFullName for list '{listName}'.");
            }
            MessageBox.Show($"ListItemEntityTypeFullName for '{listName}': {listItemEntityTypeFullName}", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

            // 准备请求体
            var itemPayload = new
            {
                __metadata = new { type = listItemEntityTypeFullName },
                Title = newItemTitle
            };
            var jsonPayload = JsonConvert.SerializeObject(itemPayload);
            MessageBox.Show($"Prepared Item Payload (JSON body): {jsonPayload}", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);

            // 执行添加请求
            var addUrl = $"{siteUrl}_api/web/lists/getByTitle('{listName}')/items";
            var addRequestContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
            addRequestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
            addRequestContent.Headers.Add("X-RequestDigest", formDigestValue);

            MessageBox.Show($"Attempting to add new item to list '{listName}' via REST API...", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
            var addResponse = await client.PostAsync(addUrl, addRequestContent);
            addResponse.EnsureSuccessStatusCode();

            MessageBox.Show($"Item '{newItemTitle}' added successfully to list '{listName}'!", "SharePoint REST API", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        // 异常处理部分保持不变
        catch (HttpRequestException httpEx)
        {
            // ...原异常处理代码
        }
        catch (Exception ex)
        {
            // ...原异常处理代码
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:42:06