Access cloudant db using .Net HttpClient - c#

I am attempting to connect to Cloudant (a couch-style DB) from a .Net MVC application. I am following the guidelines for consuming a web API using the HttpClient, as illustrated here:
http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client
I have two methods so far -- one to get a document and one to create a document -- and both have errors. The Get method returns Unauthorized and the Post method returns MethodNotAllowed.
The client is created like this:
private HttpClient CreateLdstnCouchClient()
{
// TODO: Consider using WebRequestHandler to set properties
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(_couchUrl);
// Accept JSON
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
The Get method is:
public override string GetDocumentJson(string id)
{
string url = "/" + id;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
strContent = response.Content.ReadAsStringAsync().Result;
}
else
{
// DEBUG
strContent = response.StatusCode.ToString();
LslTrace.Write("Failed to get data from couch");
}
}
return strContent;
}
The Post method is:
public override string CreateDocument(object serializableObject)
{
string url = CouchApi.CREATE_DOCUMENT_POST;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.PostAsJsonAsync(url, serializableObject).Result;
strContent = response.Content.ReadAsStringAsync().Result;
}
if (response.IsSuccessStatusCode)
{
return strContent;
}
else
{
LslTrace.Write("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return response.StatusCode.ToString();
}
}
URLs are per the API documentation: https://username:password#username.cloudant.com.
I am very confused by what is going on and having a lot of trouble finding examples. Thanks for your help!
Thomas

With the HttpClient, you need to do the following to authenticate correctly (assuming you use basic auth):
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential(_userName, _password);
HttpClient client = new HttpClient(handler) {
BaseAddress = new Uri(_couchUrl)
};
You should not specify the username/password in the _couchUrl - HttpClient doesn't support that.
I can't see your implementation of PostAsJsonAsync or the complete Url your are building, but you can try inspecting / logging response.ReasonPhrase when an error occurs to get a hint as to what went wrong.

Related

Can't make Post requests to Web API

So I've looked around for an answer for this but nothing I've found even comes close to solving it.
I'm trying to set up a Post method on my Web API but no matter what I do it just gives me an internal server error.
I've tried adding [FromBody] (it's a simple type).
HttpClient client {get;set;}
public APICall()
{
client = new HttpClient
{
BaseAddress = new Uri("http://localhost:1472/api/")
};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
}
public void PostTimeTaken(long timeTaken)
{
var response = client.PostAsJsonAsync("Logging", timeTaken).Result;
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.ReasonPhrase);
}
}
and then my controller action looks like this:
public void Post([FromBody] long timeTaken)
{
_api.DataBuilder.NumberOfAPICalls += 1;
_api.DataBuilder.ResponseTimes.Add(timeTaken);
}
I get no error message that could actually explain what's going on, just "Internal server error"
------SOLVED-------
Just in case anyone stumbles across this looking for the same answer, the issue was I was sending the data to the server in an incorrect format, it needed to be ProtoBuf serialised first, code snippet for anyone it might help:
public void PostToAPI(int ThingToSend)
{
using (var stream = new MemoryStream())
{
// serialize to stream
Serializer.Serialize(stream, ThingToSend);
stream.Seek(0, SeekOrigin.Begin);
// send data via HTTP
StreamContent streamContent = new StreamContent(stream);
streamContent.Headers.Add("Content-Type", "application/x-protobuf");
var response = client.PostAsync("Logging", streamContent);
Console.WriteLine(response.Result.IsSuccessStatusCode);
}
}
using (var client = new HttpClient())
{
string url = "http://localhost:7936";
client.BaseAddress = new Uri(url);
var jsonString = JsonConvert.SerializeObject(contentValue);
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var result = await client.PostAsync("/Api/Logger/PostActionLog", content);
string resultContent = await result.Content.ReadAsStringAsync();
}
Have you tried to convert
long timeTaken to A model like;
public class TimeModel {
public long TimeTaken {get;set;}
}
public void Post([FromBody] TimeModel time){
// Do Stuff
}
Here the code of creating a simple server
baseUrl = "http://localhost:1472/"; // change based on your domain setting
using (WebApp.Start<StartUp>(url: baseUrl))
{
HttpClient client = new HttpClient();
var resp = client.GetAsync(baseUrl).Result;
}
Here some changes in your code
var requestData = new List<KeyValuePair<string, string>> // here
{
new KeyValuePair<string, string>( "Logging",timeTaken),
};
Console.WriteLine("request data : " + requestData);
FormUrlEncodedContent requestBody = newFormUrlEncodedContent(requestData);
var request = await client.PostAsync("here pass another server API", requestBody);
var response = await request.Content.ReadAsStringAsync();
Console.WriteLine("link response : " + response);
Pls add your controller
[HttpPost] // OWIN - Open Web Interface for .NET
public HttpResponseMessage Post([FromBody] long timeTaken)
{
_api.DataBuilder.NumberOfAPICalls += 1;
_api.DataBuilder.ResponseTimes.Add(timeTaken);
return Request.CreateResponse(HttpStatusCode.OK); //Using Post Method
}

Unable to send string as HTTPContent

I have created an API, which shall have the capability to connect to en external API via POST and with a request body in form of a string.
I am able to connect directly to the API from Postman without trouble.. But it does not work via my own API.
Any ideas?
This is the Pastebin.
private string EncodeExternalApiLink = "https://blabla.dk";
private string EncodeExternalApiLinkPostFilter = "searchstring/blabla/api/search";
[HttpPost("getdata/filtered")]
public async Task<IActionResult> GetDataFromExternalFiltered([FromBody] string filter)
{
var filterString = new StringContent(filter);
EncodeExternalToken token = GetExternalToken().Result;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(EncodeExternalApiLink);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.access_token);
using (var response = await client.PostAsync(EncodeExternalApiLinkPostFilter, filterString))
{
return Json(response);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
return Content(content, "application/json");
}
else
{
return NotFound();
}
}
}
}
Salutations. You might need to add a "/" to the end of your base address EncodeExternalApiLink or to the beginning of EncodeExternalApiLinkPostFilter.

C# Web API Sending Body Data in HTTP Post REST Client

I need to send this HTTP Post Request:
POST https://webapi.com/baseurl/login
Content-Type: application/json
{"Password":"password",
"AppVersion":"1",
"AppComments":"",
"UserName":"username",
"AppKey":"dakey"
}
It works great in RestClient and PostMan just like above.
I need to have this pro-grammatically and am not sure if to use
WebClient, HTTPRequest or WebRequest to accomplish this.
The problem is how to format the Body Content and send it above with the request.
Here is where I am with example code for WebClient...
private static void Main(string[] args)
{
RunPostAsync();
}
static HttpClient client = new HttpClient();
private static void RunPostAsync(){
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
Inputs inputs = new Inputs();
inputs.Password = "pw";
inputs.AppVersion = "apv";
inputs.AppComments = "apc";
inputs.UserName = "user";
inputs.AppKey = "apk";
var res = client.PostAsync("https://baseuriplus", new StringContent(JsonConvert.SerializeObject(inputs)));
try
{
res.Result.EnsureSuccessStatusCode();
Console.WriteLine("Response " + res.Result.Content.ReadAsStringAsync().Result + Environment.NewLine);
}
catch (Exception ex)
{
Console.WriteLine("Error " + res + " Error " +
ex.ToString());
}
Console.WriteLine("Response: {0}", result);
}
public class Inputs
{
public string Password;
public string AppVersion;
public string AppComments;
public string UserName;
public string AppKey;
}
This DOES NOW WORK and responses with a (200) OK Server and Response
Why are you generating you own json?
Use JSONConvert from JsonNewtonsoft.
Your json object string values need " " quotes and ,
I'd use http client for Posting, not webclient.
using (var client = new HttpClient())
{
var res = client.PostAsync("YOUR URL",
new StringContent(JsonConvert.SerializeObject(
new { OBJECT DEF HERE },
Encoding.UTF8, "application/json")
);
try
{
res.Result.EnsureSuccessStatusCode();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
You are not properly serializing your values to JSON before sending. Instead of trying to build the string yourself, you should use a library like JSON.Net.
You could get the correct string doing something like this:
var message = JsonConvert.SerializeObject(new {Password = pw, AppVersion = apv, AppComments = acm, UserName = user, AppKey = apk});
Console.WriteLine(message); //Output: {"Password":"password","AppVersion":"10","AppComments":"","UserName":"username","AppKey":"dakey"}
var client = new RestClient("Your URL");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("apk-key", apk);
//Serialize to JSON body.
JObject jObjectbody = new JObject();
jObjectbody.Add("employeeName", data.name);
jObjectbody.Add("designation", data.designation);
request.AddParameter("application/json", jObjectbody, ParameterType.RequestBody);
try
{
var clientValue= client.Execute<Response>(request);
return RequestResponse<Response>.Create(ResponseCode.OK, "", clientValue.Data);
}
catch (Exception exception)
{
throw exception;
}
I made a tools to do it quick and easy:
Install-Package AdvancedRestHandler
or
dotnet add package AdvancedRestHandler
AdvancedRestHandler arh = new AdvancedRestHandler("https://webapi.com/baseurl");
var result = await arh.PostDataAsync<MyLoginResponse, MyLoginRequest>("/login", new MyLoginRequest{
Password = "password",
AppVersion = "1",
AppComments = "",
UserName = "username",
AppKey = "dakey"
});
public class MyLoginRequest{
public string Password{get;set;}
public string AppVersion{get;set;}
public string AppComments{get;set;}
public string UserName{get;set;}
public string AppKey{get;set;}
}
public class MyLoginResponse {
public string Token{get;set;}
}
Extra:
One other thing you can do is to use ArhResponse:
Either this way, in the class definition:
public class MyLoginResponse: ArhResponse
{
...
}
Or this way, in the API call:
var result = await arh.PostDataAsync<ArhResponse<MyLoginResponse>, MyLoginRequest> (...)
and instead of try or cache, check your API call state using simple if statements:
// check service response status:
if(result.ResponseStatusCode == HttpStatusCode.OK) { /* api receive success response data */ }
// check Exceptions that may occur due to implementation change, or model errors
if(result.Exception!=null) { /* mostly serializer failed due to model mismatch */ }
// have a copy of request and response, in case the service provider need your request response and they think you are hand writing the service and believe you are wrong
_logger.Warning(result.ResponseText);
_logger.Warning(result.RequestText);
// Get deserialized verion of, one of the fallback models, in case the provider uses more than one type of data in same property of the model
var fallbackData = (MyFallbackResponse)result.FallbackModel;
Header Possible Issue
There are cases that the Server does not accept C# request due to the header that the HttpClient generates.
It is because HttpClient by default uses the value of application/json; charset=utf-8 for Content-Type...
For sending only application/json part as Content-Type and ignore the ; charset=utf-8 part, you can do as following:
For HttpClient you can fix it by looking into this thread: How do you set the Content-Type header for an HttpClient request?
As for (AdvancedRestHandler) ARH, I fixed it due to integration with some company, but I don't remember fully... I did it, either through options like of requests or through resetting the header value.
we will use HttpPost with HttpClient PostAsync for the issue.
using System.Net.Http;
static async Task<string> PostURI(Uri u, HttpContent c)
{
var response = string.Empty;
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.PostAsync(u, c);
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
return response;
}
We will call it by creating a string that we will use to post:
Uri u = new Uri("http://localhost:31404/Api/Customers");
var payload = "{\"CustomerId\": 5,\"CustomerName\": \"Pepsi\"}";
HttpContent c = new StringContent(payload, Encoding.UTF8, "application/json");
var t = Task.Run(() => PostURI(u, c));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();

Jira Rest API login error in JIRA SERVER using C#

I want connect to jira server using C# Rest api
https://jira.myserver.co.kr/rest/auth/1/session
enter code here
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "application/json";
request.Method = method;
... more
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
the remote server returned an error (404)
I tried different work arounds but all went in vain. May I know why this error is coming? What could be the resolution of this error?
You can search for a reason of this error in different ways:
by looking at logs of JIRA server, there might be some messages/stacktraces there (for example, atlasian-jira.log);
by using some tool to perform/debug/test REST calls (for example, postman), and when it's start working in tool you can write code to do it programmatically. JIRA can return description of error in the response, and tool can show it to you.
When you get this information it can give you exact reason why it is not working. Once I got 403 error and it was because threshold of unsuccessful login attempts was exceeded, I logged into JIRA server using web browser (and entered captcha), and after that I was able to obtain session through application code.
I can successfully obtain session from JIRA in the following way using postman:
Request type: POST
URL: https://myjiraserver.com/rest/auth/1/session
Body: {"username":"myusername","password":"mypassword"}
Headers: Content-Type:application/json
you can do something like this:
namespace YOUR_NAME_SPACE
{
public class jira
{
public static string createTicket(string url, string data)
{
try
{
var client = new System.Net.Http.HttpClient();
string base64Credentials = GetEncodedCredentials();
var header = new AuthenticationHeaderValue("Basic", base64Credentials);
client.DefaultRequestHeaders.Authorization = header;
var content = new StringContent(data, Encoding.UTF8, "application/json");
var result = client.PostAsync(url, content).Result;
var response = result.Content.ReadAsStringAsync().Result;
// You can call putIssue if you want
return response;
}
catch (System.Net.WebException ex)
{
Console.WriteLine("Exception Occurred" + " : {0}", ex.Message);
throw;
}
}
private static string GetEncodedCredentials()
{
string mergedCredentials = string.Format("{0}:{1}", "LOGIN", "PASSWD");
byte[] byteCredentials = UTF8Encoding.UTF8.GetBytes(mergedCredentials);
return Convert.ToBase64String(byteCredentials);
}
public static string jiraSerialise(string project, string summary, string description, string issutype, string author)
{
JObject valuesToJson =
new JObject(
new JProperty("fields",
new JObject(
new JProperty("project",
new JObject(new JProperty("key", project))),
new JProperty("summary", summary),
new JProperty("description", description),
new JProperty("issuetype",
new JObject(new JProperty("name", issutype))),
new JProperty("assignee",
new JObject(new JProperty("name", author))))));
return valuesToJson.ToString();
}
public static string putSerialize(string key, string value)
{
JObject valueToJson =
new JObject(
new JProperty(key, value));
return valueToJson.ToString();
}
public static string putIssue(string response, string author, System.Net.Http.HttpClient client)
{
JObject jsonResponse = JObject.Parse(response);
Dictionary<string, string> dictResponse = jsonResponse.ToObject<Dictionary<string, string>>();
string issueUrl = dictResponse.Last().Value;
string issueAssignee = issueUrl + "/assignee";
var authorContent = new StringContent(author, Encoding.UTF8, "application/json");
var authorResult = client.PutAsync(issueAssignee, authorContent).Result;
var authorResponse = authorResult.Content.ReadAsStringAsync().Result;
Console.WriteLine(authorResponse);
return authorResponse;
}
}
}
And now you can call this class like that:
string data = jira.jiraSerialise("lala", "nameVulnerabilty", "descriptionField", "Bug", "author");
string url = "http://YOUR_URL/rest/api/2/issue/";
Console.WriteLine(jira.createTicket(url, data));
Hope it helps :)

SendAsync using HttpClient in Xamarin Forms not awaiting

I am busy developing a Xamarin forms application and have been struggling the last 2 days to get a successful post to my webApi from the application. Posting to the WebApi from the chrome Postman app works perfectly but I cannot seem to get it going from the application.
This is the webApi method that I am trying to call:
[HttpPost]
public string Authenticate(HttpRequestMessage request)
{
string JsonObj = request.Content.ReadAsStringAsync().Result;
AuthToken _authToken = JsonConvert.DeserializeObject<AuthToken>(JsonObj);
int UserID = DomainAuth.ValidateDomainAccount(_authToken.Username, _authToken.Password);
if(UserID > 0)
{
_authToken.UserID = UserID;
_authToken.Authenticated = true;
}
else
{
switch(UserID)
{
case -99:
_authToken.AuthMessage = "The entered domain account does not exist";
break;
case -98:
_authToken.AuthMessage = "The entered domain account does not have application access";
break;
case -97:
_authToken.AuthMessage = "Incorrect username and password combination";
break;
}
}
return JsonConvert.SerializeObject(_authToken);
}
I was originally trying to post to IIS Express but read on a post that this cannot be done when using an external device so the api is hosted in IIS now.
This is the method that my viewmodel command calls:
public async void Login()
{
Tuple<bool, string> AuthCheck = await _authService.AuthenticateDomainAccount(_username, _password);
if (AuthCheck.Item1) //Item1: True / False value indicating if the login was a success
{
Helpers.Settings.Username = _username;
await _navigationService.NavigateAsync("Synchronization");
}
else
{
FeedbackMsg = AuthCheck.Item2; // Item2: Message indicating why the login has failed.
}
}
And lastly this is the method that makes the api call:
public async Task<Tuple<bool, string>> AuthenticateDomainAccount(string _Username, string _Password)
{
AuthToken _authToken = new AuthToken { Username = _Username, Password = _Password };
HttpClientHandler handler = new HttpClientHandler();
using (var client = new HttpClient(handler, false))
{
client.BaseAddress = new Uri(BaseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "User/Authenticate"))
{
req.Content = new StringContent(JsonConvert.SerializeObject(_authToken), Encoding.UTF8, "application/json");
try
{
var response = await client.SendAsync(req);
Debug.WriteLine("################# HERE I AM ################");
var result = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
string a = "a";
}
}
}
return null;
}
When I step through the application it hits the SendAsync line, but instead of waiting for it to return as expected, it skips the lines below it and immediately returns null as per the final line of the method.
Can anyone shed some light on this?
I think the issue over here is with the Uri you are trying to hit. SendAsync method may be throwing a 404 because of incorrect Url.
Please change the following line:
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "User/Authenticate"))
to
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "api/User/Authenticate"))
assuming your BaseAddress ends with a '/' at the end, else it will be
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "/api/User/Authenticate"))

Categories

Resources