I am trying to do File Copy operation in c# .net core using Microsoft graph API.
It is an asynchronous operation, and by doc, it says it returns a location in the response header to check the status of the operation,
Now the issue is I need its response header so that I can check the status of file copy operation but every time I am getting 'null' as value, I have tried following code,
DriveItem response = await graphClient.Sites[siteId].Drive.Items[itemId]
.Copy(fileName, parentReference)
.Request()
.PostAsync();
The driveItem returns null but I think at least it should have returned the additional data-carrying response status and location.
When I use online graph api it just works fine returning response and location, but it doesn't with graph client service.
Apparently it is an issue with msgraph-sdk-dotnet, at least it could be reproduced in 3.8.0 version, the error occurs while deserializing HTTP response. Probably it would be more beneficial to report it as a bug in referenced repository.
Meanwhile you could consider to construct a request for Copy a DriveItem endpoint and process response (including extracting Location header) as demonstrated below:
var message = graphClient.Sites[siteId].Drive.Items[itemId]
.Copy(fileName, parentReference)
.Request()
.GetHttpRequestMessage();
message.Method = HttpMethod.Post;
var body = new DriveItemCopyRequestBody {Name = fileName, ParentReference = parentReference};
message.Content = new StringContent(graphClient.HttpProvider.Serializer.SerializeObject(body));
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = graphClient.HttpProvider.SendAsync(message).Result;
Console.Write(response.Headers.Location);
Related
My objective is to process emails in a folder and then move them to another folder to which I have the id. To ease the workload I'm trying to make use of the batch functionality.
Unfortunately, every time I try to run the batch function I'm presented with an exception with the message Code: invalidRequest Message: Unable to deserialize content..
The code in question, simplified for just one request, can be found below.
var batch = new BatchRequestContent();
var json = JsonSerializer.Serialize( new { destinationId = Graph.Me.Messages[messageId].Move(folderId).Request().RequestBody.DestinationId } );
var jsonMessage = Graph.HttpProvider.Serializer.SerializeAsJsonContent(json);
var request = Graph.Me.Messages[messageId].Move(folderId).Request().GetHttpRequestMessage();
request.Method = HttpMethod.Post;
request.Content = jsonMessage;
batch.AddBatchRequestStep(request);
var res = await Graph.Batch.Request().PostAsync(batch);
I've narrowed down the problem to be about the request.Content because without that it will go through, though getting back with a 400 error about missing body.
Copying the string from batch.ReadAsStringAsync() and pasting that directly into the Graph Explorer and using that to run the query returns a 200 success.
Based on what I've tried I'm starting to lean on it being a limitation of the SDK's batch.
Any ideas?
While this wasn't the solution I was looking for, it works as a band aid solution.
Basically, after you add all your steps to the BatchRequestContent you use the ReadAsStringAsync() to get the body of the request. Then you use the library itself to send the HTTP Request, as instructed here.
HttpRequestMessage hrm = Graph.Batch.Request().GetHttpRequestMessage();
hrm.Method = HttpMethod.Post;
hrm.Content = Graph.HttpProvider.Serializer.SerializeAsJsonContent(await batch.ReadAsStringAsync());
// Authenticate (add access token) our HttpRequestMessage
await Graph.AuthenticationProvider.AuthenticateRequestAsync(hrm);
// Send the request and get the response.
HttpResponseMessage res = await Graph.HttpProvider.SendAsync(hrm);
I am trying to implement a Xamarin app that works with the Asana API.
I have successfully implemented the OAuth as documented in the Asana documentation here... at least I assume it is successful. I get an access token from the token endpoint in an HTTPResponse with HTTP Status "OK".
But then when I turn around and try to make an API call with that same access token, I get a 403 Forbidden error. I tried the same API call in my browser (after logging in to Asana), and it works fine, which leads me to believe that I do have access to the resource, I must have an issue with authorizing the request on my end.
The API call in question is (documented here): https://app.asana.com/api/1.0/workspaces.
My C# code is as follows (abbreviated to relevant parts, and assume that ACCESS_TOKEN contains the access token I got from the token exchange endpoint):
HttpClient client = new HttpClient();
client.BaseAddress = "https://app.asana.com/api/1.0";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", ACCESS_TOKEN);
client.DefaultRequestHeaders.Add("Accept", "application/json");
And then I use this HttpClient (named client) in the following function:
// Returns a list of the Asana workspace names for the logged in user.
private async Task<List<string>> GetWorkspacesAsync()
{
List<string> namesList = new List<string>();
// Send the HTTP Request and get a response.
this.UpdateToken(); // Refreshes the token if needed using the refresh token.
using (HttpResponseMessage response = await client.GetAsync("/workspaces"))
{
// Handle a bad (not ok) response.
if (response.StatusCode != HttpStatusCode.OK)
{
// !!!THIS KEEPS TRIGGERING WITH response.StatusCode AS 403 Forbidden!!!
// Set up a stream reader to read the response.
// This is for TESTING ONLY
using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
{
// Extract the json object from the response.
string content = reader.ReadToEnd();
Debug.WriteLine(content);
}
throw new HttpRequestException("Bad HTTP Response was returned.");
}
// If execution reaches this point, the Http Response returned with code OK.
// Set up a stream reader to read the response.
using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
{
// Extract the json object from the response.
string content = reader.ReadToEnd();
JsonValue responseJson = JsonValue.Parse(content);
foreach (JsonValue workspaceJson in responseJson["data"])
{
string workspaceName = workspaceJson["name"];
Debug.WriteLine("Workspace Name: " + workspaceName);
namesList.Add(workspaceName);
}
}
}
// I have other awaited interactions with app storage in here, hence the need for the function to be async.
return namesList;
}
Finally found the answer. It looks like I was using HttpClient incorrectly; a subtle thing that should be equivalent, but is not due to the way it is implemented.
The answer
I needed to place the final slash at the end of the BaseAddress property of HttpClient, and NOT at the start of the relative address for the specific request. This answered question explains this.
To fix my code
I needed to change the setting up of the BaseAddress:
HttpClient client = new HttpClient();
client.BaseAddress = "https://app.asana.com/api/1.0/"; // FINAL SLASH NEEDED HERE
And remove the slash from the request's relative address:
// DO NOT put slash before relative address "workspaces" here
using (HttpResponseMessage response = await client.GetAsync("workspaces"))
Why I got the original error
When HttpClient combined the BaseAddress with the relative URI I specified in GetAsync(), it dropped off some of the base address, since the final slash was not included. The resulting address from combining the BaseAddress with the relative URI was a valid URL, but not a valid page/API call in Asana. Asana thus did an automatic redirect to a login page, which, of course, the rest of the API call would be forbidden from there.
How I discovered this
In debugging, I grabbed the access token returned during my app's authorization with Asana. I then recreated the request to the "/workspaces" API myself in Postman, and the request worked as expected. This confirmed that my authorization worked fine, and the issue must be with the specific request rather than the authorization. In debugging I then looked into the HttpResponseMessage, which has a property called RequestMessage, that includes the actual URL the GetAsync() made the request against. I observed the Login URL from Asana, rather than the BaseAddress I specified... which led me to the question/
answer linked above.
Hope this explanation helps anyone who comes across a similar error!
I have a loop that will loop through records in my DB, pulling information i need and then creating 3 folders & upload a file.
This works OK for like 40 records but then it starts erroring out with the below response back from sharepoint: <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">\r\n<HTML><HEAD><TITLE>Bad Request</TITLE>\r\n<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=us-ascii\"></HEAD>\r\n<BODY><h2>Bad Request - Header Field Too Long</h2>\r\n<hr><p>HTTP Error 400. A request header field is too long.</p>\r\n</BODY></HTML>
I am not sure whats going on, i read online its todo with cookies but i am using HTTPClient to send the request so i dont know how that would effect it? I also seen onlne about changing the kestrel?
Can anybody shed some light on this for me? Provide me with an easy but working solution? I dont use CSOM for integrating to sharepoint online, i use HTTP Requests, below is a sample of how i interact with sharepoint.
It seems as if i get blocked or banned temporarily cause if i wait a good bit, i can then make the same request that failed previously, and it will work! So strange.
Sample code (Used to create a resource at Sharepoint):
//Set Endpoint
var sharePointEndpoint = $"https://{hostname}/sites/{site}/_api/web/folders";
//Set default headers
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", sharePointToken); //Set token
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
//Pre-Body data setup
var metaData = new MetaDataModel();
metaData.type = "SP.Folder";
//Body data setup
var bodyModel = new ExpandoObject() as IDictionary<string, object>;
bodyModel.Add("__metadata", metaData);
bodyModel.Add("ServerRelativeUrl", location + "/" + directoryName + "/");
//Set content headers
HttpContent strContent = new StringContent(JsonConvert.SerializeObject(bodyModel));
strContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
strContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
// Send request, grab response
var response = await client.PostAsync(sharePointEndpoint, strContent);
//Return response message
return response;
It turns out I needed to use Content-Length header when sending the request, once done I was able to successfully communicate with sharepoint without encountering this error.
More information here: https://social.technet.microsoft.com/Forums/en-US/26459f1c-945d-4112-9200-69c5a33a37ff/sharepoint-online-rest-api-returns-http-error-400-a-request-header-field-is-too-long?forum=sharepointdevelopment
Thanks.
I have found similar questions both here as well as on the Elastic discussion forum, but unfortunately none of the answers helped.
I am currently using ElasticSearch 7.0.
I want to make a bulk request to my ElasticSearch server. My JSON file contains information that looks something like this:
{ "index": { "_index": "website", "_id": "link1" }}
{ "label": "Link1" }
Each line is terminated by an LF line break, and there is also an additional LF line break at the end of the document.
In C#, here is how I make a POST request for my bulk data:
HttpResponseMessage response = await httpClient.PostAsJsonAsync($"http://127.0.0.1:9200/website/_bulk", jsonDocumentContents);
And yet I keep seeing this error message:
{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"The bulk request must be terminated by a newline [\\n]"}],"type":"illegal_argument_exception","reason":"The bulk request must be terminated by a newline [\\n]"},"status":400}
How can I fix this error?
UPDATE:
A short description of how I read the JSON document contents into the jsonDocumentContents variable: The JSON document was stored inside a zipped folder, so retrieving it requires unzipping:
ZipArchive archive = new ZipArchive(zippedFolderStream);
foreach (ZipArchiveEntry entry in archive.Entries)
{
string jsonDocumentContents = new StreamReader(entry.Open()).ReadToEnd();
HttpResponseMessage response = await httpClient.PostAsJsonAsync($"http://127.0.0.1:9200/website/_bulk", jsonDocumentContents);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
UPDATE:
I just made a bulk request with the exact same contents using PostMan, and the request was successful. However, the error message persists when I make the same bulk request in C# using httpClient.PostAsJsonAsync(...).
I got it working by changing my code to the following:
ZipArchive archive = new ZipArchive(zippedFolderStream);
foreach (ZipArchiveEntry entry in archive.Entries)
{
string jsonDocumentContents = new StreamReader(entry.Open()).ReadToEnd();
StringContent content = new StringContent(jsonDocumentContents, Encoding.ASCII, mediaType: "application/json");
HttpResponseMessage response = await httpClient.PostAsync($"http://127.0.0.1:9200/website/_bulk", content);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Notice that I am using HttpClient.PostAsync() instead of HttpClient.PostAsJsonAsync(), with a StringContent instance that specifies "application/json" as its media type.
I looked into the source code for HttpClient, and noticed that a new instance of JsonMediaTypeFormatter is created every time HttpClient.PostAsJsonAsync is called.
Since my POST requests are successful when I make them through PostMan, the issue must be caused by how PostAsJsonAsync() is implemented. I suspect, but have not verified, that the problem is due to the default properties in the JsonMediaTypeFormatter class.
To circumvent the problem I decided to use Http.PostAsync() with a correctly-configured StringContent instance.
Lo and behold, I can now send bulk requests to my ElasticSearch server using C#.
trying to send Tweet with the image attached, using RestSharp:
_client = new RestClient("https://api.twitter.com")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(Key, Secret, Token, TokenSecret)
};
RestRequest request = new RestRequest("/1.1/statuses/update_with_media.json", Method.POST);
request.AddFile("media", att.File, att.FileName, "base64");
request.AddParameter("status", postStatus.Text);
var result = await _client.ExecuteTaskAsync(request);
The result is "Could not authenticate you" error no - 32
Thanks
UPDATE: All authentication parameters start from oauth_ and go in alphabetical order, the token, token secret, app key and app key secret are correct, the update status without media works perfectly.
UPDATE 2:
Solution
var request = new RestRequest("/1.1/statuses/update_with_media.json", Method.POST);
request.AlwaysMultipartFormData = true;
request.AddParameter("status", message, ParameterType.UrlSegment);
request.AddFile("media[]", file, filename, "application/octet-stream");
var result = _client.Execute(request);
This is actually a problem with restsharp 104.4 (version in Nuget as of time of writing)
We hit the same problem, but your solution above did not work for us. The UrlSegment parameter fails on a status update, and while it does not fail on a call to update_with_media, it also does not post the status, just the picture.
The problem is with OAuth1Authenticator, it does not ignore non-oauth POST or GET parameters, hence the authentication errors above, and why the URL segment parameter "works".
To fix this, get the latest version of RestSharp from GitHub and use that instead.
For those interested, the checkin involved was only made a month or so after the release and can be found here.