I try to do some CRUD operations on a sharepoint list, unfortunately I cannot update an item in the list. Since I can create and also delete items from the list, I think everything is fine with authentication and rights, but maybe I am not aware of some specific for the update process.
I have extracted the code from my libs to thrill it down to the most relevant lines, in an async method I first read the list and get the item to update
async Task Main()
{
var BaseUrl = "https://my_site/";
var credentials = new NetworkCredential("user", "pass", "domain");
string RequestDigest = null;
HttpClientHandler handler = new HttpClientHandler { Credentials = credentials };
var SpClient = new HttpClient(handler)
{
BaseAddress = new Uri(BaseUrl)
};
SpClient.DefaultRequestHeaders.Accept.Clear();
SpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var url = BaseUrl + $"_api/lists/getbytitle('Test')/items";
var response = await SpClient.GetAsync(url);
var data = await response.Content.ReadAsStringAsync();
if (response.StatusCode != HttpStatusCode.OK) throw new Exception(response.StatusCode.ToString() + " - " + response.RequestMessage.RequestUri);
SharepointListItems listEntries = JsonSerializer.Deserialize<SharepointListItems>(data);
var existing = listEntries.ListItems.FirstOrDefault(p => p.Title == "Eins");
This works fine, existing now contains the item from the list.
Now I tried to update this item:
// This will not work: StatusCode: 403, ReasonPhrase: 'FORBIDDEN', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
existing.Title = "Changed";
string jsonString = JsonSerializer.Serialize<SharepointListItem>(existing);
So I tried to set up the sting for the item "by Hand" for testing purpose:
// This will also not work : StatusCode: 403, ReasonPhrase: 'FORBIDDEN', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
// Prepare body string for testing
string jsonString = "{\"__metadata\": { \"type\": \"SP.Data.TestListItem\" }, \"Title\": \"Changed\"}";
Finally this is the code, that writes the item back to the list:
// write item back to list
if (RequestDigest == null || DateTime.Now > Convert.ToDateTime(RequestDigest.Split(',')[1]).AddSeconds(1800))
{
url = BaseUrl + "_api/contextinfo";
response = await SpClient.PostAsync(url, null);
data = response.Content.ReadAsStringAsync().Result;
var result = JsonSerializer.Deserialize<SharepointContext>(data);
RequestDigest = result.FormDigestValue;
SpClient.DefaultRequestHeaders.Remove("X-RequestDigest");
SpClient.DefaultRequestHeaders.Add("X-RequestDigest", RequestDigest);
SpClient.DefaultRequestHeaders.Add("X-HTTP-Method", "MERGE");
SpClient.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
}
if (existing.odataetag != null)
{
SpClient.DefaultRequestHeaders.Remove("If-Match");
SpClient.DefaultRequestHeaders.Add("If-Match", $"*");
}
var content = new StringContent(jsonString);
content.Headers.Clear();
content.Headers.Add("Content-Type", "application/json");
content.Headers.Add("X-RequestDigest", RequestDigest);
content.Headers.Add("X-HTTP-Method", "MERGE");
url = BaseUrl + $@"_api/lists/getbytitle('Test')/items({existing.Id})";
response = await SpClient.PostAsync(url, content);
Console.WriteLine(response.StatusCode);
response.Dump();
}
This sample I have extracted from my code and written in LinqPad. Here are the classes required for the full sample to run:
public class SharepointListItems
{
[JsonPropertyName("odata.metadata")]
public string odatametadata { get; set; }
[JsonPropertyName("value")]
public List<SharepointListItem> ListItems { get; set; }
}
public class SharepointListItem
{
public SharepointListItem() { }
[JsonPropertyName("odata.type")]
public string odatatype { get; set; }
[JsonPropertyName("odata.id")]
public string odataid { get; set; }
[JsonPropertyName("odata.etag")]
public string odataetag { get; set; }
[JsonPropertyName("odata.editLink")]
public string odataeditLink { get; set; }
public string Title { get; set; }
public int Id { get; set; }
}
public class SharepointContext
{
public string odatametadata { get; set; }
public int FormDigestTimeoutSeconds { get; set; }
public string FormDigestValue { get; set; }
public string LibraryVersion { get; set; }
public string SiteFullUrl { get; set; }
public string[] SupportedSchemaVersions { get; set; }
public string WebFullUrl { get; set; }
}
May anyone give any tips what I am doing wrong here?
As stated above the code runs fine on creating a new item. The only difference is that the body in this case only contains the item as Json without metadata and the etag header is not set.
Thank's for any hint.