Using C# to update a Confluence page I keep getting 405 ERROR - c#

I am trying to update a Confluence page.
I have been able to use this on Confluence localhost, but when I tried it on the production server I got this error:
StatusCode: 405, ReasonPhrase: 'Method Not Allowed', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Vary: Accept-Encoding
Date: Tue, 31 Jan 2017 21:29:44 GMT
Server: Apache/2.2.15
Server: (CentOS)
Content-Length: 342
Allow: GET
Allow: HEAD
Allow: POST
Allow: OPTIONS
Allow: TRACE
Content-Type: text/html; charset=iso-8859-1
}
This is my code. Any idea what would be causing this issue?
string json = "{\"version\":{\"number\":4},\"title\":\"Bloomberg Test\",\"type\":\"page\",\"body\":{\"storage\":{\"value\":\"Hello World\",\"representation\": \"storage\"}}}";
string userpass = username+":"+password;
string encoded = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(userpass));
string encval = "Basic " + encoded;
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Atlassian-Token", "nocheck");
client.DefaultRequestHeaders.Add("Authorization", encval);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.BaseAddress = new Uri(baseurl);
var resp = client.PutAsync(#"/rest/api/content/"+pageid, content);

405 means that the HTTP method (GET, POST, PUT ...) is not allowed.
I don't know the details of Confluence, but try using a POST request
var resp = client.PostAsync(#"/rest/api/content/"+pageid, content);

You're performing an HTTP Put via client.PutAsync(). This is probably allowed locally, but on the server it's not. The response even includes the allowed http methods:
Allow: GET
Allow: HEAD
Allow: POST
Allow: OPTIONS
Allow: TRACE
So if the suggested PostAsync() is not supported by Confluence, adjust the server and allow the PUT method as well.

Replaced:
client.BaseAddress = new Uri(baseurl);
var resp = client.PutAsync(#"/rest/api/content/"+pageid, content);
With:
var resp = client.PutAsync(baseurl+"/rest/api/content/"+pageid, content);
My guess, the BaseAddress is doing something odd like adding a slash at the end or something.
It works now!

Related

Salesforce Account Creation via REST POST API Is Always Failing With Error "Bad Request" Via C#

I'm trying to write a simple console based application with the help from C# to create Salesforce standard objects like Accounts, Contacts, Leads etc. As a starting point I tried to create account using following code but it is always failing with below reason.
Code Snippet -
// Here string requestbody = "{\"name\":\"Testrun Limited\", \"city\":\"Delhi\"}"
public static string SFPostCall2(string requestbody)
{
string responseresult = string.Empty;
using ( var client = new HttpClient())
{
// Headers
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", SalesforceClient.AuthToken);
client.DefaultRequestHeaders.Add("X-PrettyPrint", "1");
// Contents
Uri URL = new Uri("https://mysalesforce-env.salesforce.com/services/data/v55.0/sobjects/account");
var data = new StringContent(requestbody, Encoding.UTF8, "application/json");
var response = client.PostAsync(URL, data).Result;
response.EnsureSuccessStatusCode();
responseresult = response.Content.ReadAsStringAsync().Result;
}
return responseresult;
}
Error Details -
System.Net.Http.HttpRequestException: 'Response status code does not
indicate success: 400 (Bad Request).'
Response Body -
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Date: Wed, 29 Jun 2022 12:08:46 GMT
Set-Cookie: CookieConsentPolicy=0:1; path=/; expires=Thu, 29-Jun-2023 12:08:46 GMT; Max-Age=31536000
Set-Cookie: LSKey-c$CookieConsentPolicy=0:1; path=/; expires=Thu, 29-Jun-2023 12:08:46 GMT; Max-Age=31536000
Set-Cookie: BrowserId=OfVYGvekEeyahU8Wnegiow; domain=.salesforce.com; path=/; expires=Thu, 29-Jun-2023 12:08:46 GMT; Max-Age=31536000
Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Robots-Tag: none
Cache-Control: no-store, must-revalidate, no-cache, max-age=0, private
Sforce-Limit-Info: api-usage=16/15000
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
}
}
Please Note :-
I have successfully got the Salesforce Token while using the GET request.
Same request with same parameters in POSTMAN is creating data successfully every time
I have also tried using
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls but still no
help
Same request via JavaScript is also creating data from IE browser { to avoid CORS error }.
I have also tried to change the content to JSON object using following code, but still got same error -
var dataobj = new
{
Name = "Testrun Limited",
City = "Delhi"
};
var jsondataobj = JsonConvert.SerializeObject(dataobj);
HttpContent c_content = new StringContent(jsondataobj, Encoding.UTF8, "application/json");
This style worked for me.
HttpRequestMessage requestUpdate = new HttpRequestMessage(HttpMethod.Post, url);
requestUpdate.Headers.Add("Authorization", "Bearer " + SFAuthToken);
requestUpdate.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
requestUpdate.Content = contentUpdate;
HttpClient client = new HttpClient();
HttpResponseMessage asyncResponse = client.SendAsync(requestUpdate).Result;
var content = asyncResponse.Content.ReadAsStringAsync();

Google API Oauth2 Refresh token bad request 400

having an issue refreshing an existing token by using the Google API with Xamarin C#. I guess something is wrong with my code, but I cannot find what.
(Note: I added newline characters at the & to make the requests more readable. Hope everyone will be cool with that.)
Request Auth URI
https://accounts.google.com/o/oauth2/v2/auth?
scope=https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events&
include_granted=true&
response_type=code&
redirect_uri=com.googleusercontent.apps.MYOAUTHCLIENTID:/oauth2redirect&
client_id=MYOAUTHCLIENTID.apps.googleusercontent.com&
access_type=offline
Redirect URI
com.googleusercontent.apps.MYOAUTHCLIENTID:/oauth2redirect
Response Authentication Code
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Request token URI
https://oauth2.googleapis.com/token?
code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&
redirect_uri=com.googleusercontent.apps.MYOAUTHCLIENTID:/oauth2redirect&
client_id=MYOAUTHCLIENTID.apps.googleusercontent.com&
grant_type=authorization_code
Response token
{
"access_token": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"expires_in": 3599,
"refresh_token": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
"scope": "https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events",
"token_type": "Bearer"
}
Now I can use the "access_token" in my HTTP header requests and work with the calendar without problems. I also can revoke the token, but not refreshing it.
Try to refresh token
var dict = new Dictionary<string, string>();
dict.Add("Content-Type", "application/x-www-form-urlencoded");
var contentHeader = new FormUrlEncodedContent(dict);
HttpClient refreshTokenClient = new HttpClient();
// Doing this because someone wrote that this helped in his case. Did not help :/
System.Net.ServicePointManager.SecurityProtocol |=
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls;
// uriTokenRequest has the type URI with this value
// {https://oauth2.googleapis.com/token?
// code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&
// client_id=MYOAUTHCLIENTID.apps.googleusercontent.com&
// grant_type=refresh_token&
// refresh_token=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ}
using (var result = await refreshTokenClient.PostAsync(uriTokenRequest, contentHeader))
if (result.IsSuccessStatusCode) // This will be FALSE
{
...
}
The response
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Accept-Ranges: none
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Date: Sun, 11 Jul 2021 13:46:09 GMT
Pragma: no-cache
Server: scaffolding
Server: on
Server: HTTPServer2
Transfer-Encoding: chunked
Vary: X-Origin
Vary: Referer
Vary: Origin
Vary: Accept-Encoding
X-Android-Received-Millis: 1626011170088
X-Android-Response-Source: NETWORK 400
X-Android-Selected-Protocol: http/1.1
X-Android-Sent-Millis: 1626011170050
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
Content-Type: application/json; charset=utf-8
Expires: Mon, 01 Jan 1990 00:00:00 GMT
}}
The Google API documentation https://developers.google.com/identity/protocols/oauth2/native-app#offline does say about a "client_secret". But with the response_type "code" I never received such. I've read the documentation now so many times that I am blind to it.
Or does "code" not require a token refresh?
Any ideas?
EDIT: "code" does require a token refresh, since the token does expire, as the result property "expires_in" already tell.
EDIT 2: As by Cherry Bu - MSFT suggested I peeked at the Xamarin.Auth source code and found some differences, that I adapted. Unfortunately I am still not able to succeed. This is my latest try:
var queryValues = new Dictionary<string, string>();
//queryValues.Add("Content-Type", "application/x-www-form-urlencoded");
queryValues.Add("client_id", Constants.OAuthClientId);
queryValues.Add("code", _authenticationCode);
queryValues.Add("refresh_token", _refresh_token);
queryValues.Add("grant_type", "refresh_token");
var httpContent = new FormUrlEncodedContent(queryValues);
HttpClient refreshTokenClient = new HttpClient();
//if (!string.IsNullOrEmpty(_accessToken) && !string.IsNullOrEmpty(_accessTokenType))
//{
// refreshTokenClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(_accessTokenType, _accessToken);
//}
using (var response = await refreshTokenClient.PostAsync("https://accounts.google.com/o/oauth2/token", httpContent).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode) --> This will be false
{
...
}
}
The correct http end point to refresh an access token can be found below
HTTP POST https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
I actually have an example of using the google .net client library with Xamarin i had to adapt the authorization to it. I will have a look around see if i can find the code.
Fixed it. 2 issues:
1 The DefaultRequestHeader did not work.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(GoogleOAuth.AccessToken, GoogleOAuth.AccessTokenType);
Even when the object itself did look alright it did not work. I replaced it with that:
HttpClient client = new HttpClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Post, calenderUri);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(GoogleOAuth.AccessTokenType, GoogleOAuth.AccessToken); // both are strings. ("Bearer", "yaa...")
requestMessage.Content = data;
var response = await client.SendAsync(requestMessage);
if (!response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
throw new Exception(content);
}
After that I only had to do one more thing
2 Removed "code" and "client_secret" from the request data. The result:
using (HttpClient client = new HttpClient())
{
var googleData = new Dictionary<string, string>();
googleData.Add("client_id", GoogleOAuth.ClientId);
googleData.Add("refresh_token", GoogleOAuth.RefreshToken);
googleData.Add("grant_type", "refresh_token");
var data = new FormUrlEncodedContent(googleData);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, Constants.GoogleTokenUri);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(GoogleOAuth.AccessTokenType, GoogleOAuth.AccessToken);
requestMessage.Content = data;
var response = await client.SendAsync(requestMessage);
if (response.IsSuccessStatusCode)
{
// do something
}
}
For weeks I tried to get it running and was close to insanity. Hope this saves someones nerves.

Azure graph API / c# patch URL

I am attempting to write a password reset application c# and the graph API. I have set permissions for the application within Azure, receive a valid token, and can request information.
I am receiving a 400 Bad Request response when attempting to perform the reset. I believe I am forming the URL incorrectly. Here is the response I receive followed by my code.
Thanks in advance!
Response: StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
request-id: omitted
client-request-id: omitted
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Central US","Slice":"SliceA","Ring":"3","ScaleUnit":"002","Host":"AGSFE_IN_29","ADSiteName":"CHI"}}
Duration: 43.0949
Strict-Transport-Security: max-age=31536000
Cache-Control: private
Date: Mon, 02 Apr 2018 18:06:06 GMT
Content-Type: application/json
}
private static async Task ResetPasswordAsync(HttpClient client, string UPN)
{
var payload = new
{
accountEnabled = true,
passwordProfile = new
{
forceChangePasswordNextSignIn = true,
password = "Password!"
}
};
var payloadJSON = JsonConvert.SerializeObject(payload);
Console.WriteLine(payloadJSON);
HttpMethod method = new HttpMethod("PATCH");
string requestUrl = $"https://graph.microsoft.com/v1.0/users/{UPN}?api-version=1.6";
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent($"{payloadJSON}", Encoding.UTF8, "application/json")
};
var response = await client.SendAsync(request);
Console.WriteLine("Response: " + response);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(response.ReasonPhrase);
}
}
According to 400 Bad Request, we could know that there is something wrong with http request. In your case, you could use Fiddler to catch the htt prequest, we could get Query parameter api-version not allowed. So you could remove the api version from the requesturl.
We could get more information about update use from Graph Update user API
PATCH /users/{id | userPrincipalName}
We also need to add the Authorization in the request header.
string requestUrl = $"https://graph.microsoft.com/v1.0/users/{UPN}";
var token ="Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCSGg0...."
...
request.Headers.Add("Authorization", token);
var response = await client.SendAsync(request);

HttpClient.PutAsync - "Entity only allows writes with a JSON Content-Type header"

I have a POST method that can post data perfectly.
Looking at docs it seems a PATCH (or PUT) should look the exact same, just use PutAsync instead of PostAsync.
Well doing just that I get the following error:
+ postResponse {StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.NoWriteNoSeekStreamContent, Headers:
{
Cache-Control: private
Date: Mon, 09 Oct 2017 12:19:28 GMT
Transfer-Encoding: chunked
request-id: 60370069-f7c4-421d-842e-b1ee8573c2c2
client-request-id: 60370069-f7c4-421d-842e-b1ee8573c2c2
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Europe","Slice":"SliceB","ScaleUnit":"002","Host":"AGSFE_IN_7","ADSiteName":"DUB"}}
Duration: 3.2626
Content-Type: application/json
}} System.Net.Http.HttpResponseMessage
And response:
Entity only allows writes with a JSON Content-Type header
And sure enough in the Error I can also see this:
ContentType {text/plain; charset=utf-8} System.Net.Http.Headers.MediaTypeHeaderValue
So the error makes sense, however, I do tell it to use JSON, and it work as intended in my POST-method with the same code:
public async Task UpdateToGraph(object UnSerializedContent, string relativeUrl)
{
string accessToken = await _tokenManager.GetAccessTokenAsync();
HttpContent content = new StringContent(JsonConvert.SerializeObject(UnSerializedContent));
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
Client.DefaultRequestHeaders.Add("ContentType", "application/json");
string endpoint = "https://graph.microsoft.com/v1.0" + relativeUrl;
var postResponse = Client.PutAsync(endpoint, content).Result;
string serverResponse = postResponse.Content.ReadAsStringAsync().Result;
}
You can use the .{Verb}AsJsonAsync HttpClientExtensions methods.
public async Task UpdateToGraph(object UnSerializedContent, string relativeUrl) {
var accessToken = await _tokenManager.GetAccessTokenAsync();
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
Client.DefaultRequestHeaders.Add("ContentType", "application/json");
var endpoint = "https://graph.microsoft.com/v1.0" + relativeUrl;
var postResponse = await Client.PutAsJsonAsync(endpoint, UnSerializedContent);
var serverResponse = await postResponse.Content.ReadAsStringAsync();
}
Also note the proper use of async/await by not mixing blocking calls like .Result with async methods as this can lead to deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
Set the content type using the StringContent constructor:
HttpContent content = new StringContent(JsonConvert.SerializeObject(UnSerializedContent), System.Text.Encoding.UTF8, "application/json");
As far as I'm aware, you're not meant to set the content header on the request object when using the HttpClient.

Unsupported Media Type when using Web API

I want to use public Web Api from: https://www.xbtce.cc/tradeapi.
So here is a code:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var url = "https://cryptottdemowebapi.xbtce.net:8443/api/v1/public/tick";
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
And result is:
{StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 15 Aug 2016 16:03:45 GMT
Content-Length: 0
}}
This error continue even after adding request headers with "application/json".
Any ideas how to fix this?
Seems like a broken server implementation, given if I load that URL in my browser, it most definitely returns application/json as the Content-Type for the response. It's not strictly correct, but might work for this (seemingly) broken server to pass an Accept header with wildcards, e.g. */* and see if that helps.
You forgot about the gzip
below the correct code
var client = new HttpClient(new PublicContentHandler());
client.BaseAddress = new Uri(_webApiAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
public class PublicContentHandler : HttpClientHandler
{
public PublicContentHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
}
Refer the link with the bunch of c# examples.

Categories