I'm learning web api, well trying to - I've hit a snag. I can post to it fine and get a response when there are no parameters in the method on the api, e.g this works...
client
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + T.Access_Token);
var req = new HttpRequestMessage(HttpMethod.Post, "https://baseurl/api/controller/sites");
var response = await client.SendAsync(req);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
List<Site_2016> sites = Library.Data.Json.FromJson<List<Site_2016>>(result);
return sites;
}
else return new List<Site_2016>();
}
Api
[HttpPost]
[Route("sites")]
public async Task<IHttpActionResult> GetSites()
{
//Do stuff
return Ok(Sites);
}
But I can't for the life of me get it to work when I pass through some parameters like so...
client
using (var client = new HttpClient())
{
Dictionary<String, String> dict = new Dictionary<string, string>();
dict.Add("identifier", instance);
dict.Add("endOfDay", IsEndOfDayOnly.ToString());
var content = new FormUrlEncodedContent(dict);
client.BaseAddress = new Uri("https://baseurl/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + T.Access_Token);
var response = await client.PostAsync("api/controller/lastsaledate", content);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
KeyValuePair<String, DateTime> LastPush = Library.Data.Json.FromJson<KeyValuePair<String, DateTime>>(result);
return LastPush;
}
else
{
return new KeyValuePair<string, DateTime>();
}
}
Api
[HttpPost]
[Route("lastsaledate")]
public async Task<IHttpActionResult> GetLastSalesUpdate(string identifier, bool endOfDay)
{
//do stuff
return Ok(Result);
}
The response returns 404, I was wandering if it could be a routing issue perhaps? The bearer token is valid for both posts, if I disable authorization then I get the same result. The api/controller/action url is definitely correct.
Yes, in this case in particular you can try this
var response = await client.PostAsync(string.format("api/controller/lastsaledate?identifier={0}&endOfDay{1}",instance,IsEndOfDayOnly), content);
And remove the dict
Or you can try this
[HttpPost]
[Route("lastsaledate")]
public async Task<IHttpActionResult> GetLastSalesUpdate([FromBody]string identifier, [FromBody]bool endOfDay)
{
//do stuff
return Ok(Result);
}
Or you can try this
[HttpPost]
[Route("lastsaledate")]
public async Task<IHttpActionResult> GetLastSalesUpdate(testClass myParam)
{
//do stuff
return Ok(Result);
}
public class testClass
{
public string identifier;
public bool endOfDay;
}
Related
I have built an api caller which, for post requests, handles an input and output object:
public static async Task<TResponse> CallPostWebApi<TRequest, TResponse>(TRequest request, TResponse response, string serviceUrl)
{
using var client = new HttpClient { BaseAddress = new Uri(serviceUrl) };
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var json = JsonConvert.SerializeObject(request);
var formData = new StringContent(json, Encoding.UTF8, "application/json");
var result = await client.PostAsync(serviceUrl, formData);
if (!result.IsSuccessStatusCode)
{
return response;
}
var data = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(data);
}
my api now returns an IActionResult with the object within it:
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Route("[Action]")]
public IActionResult MethodName(MyModel model)
{
try
{
var result = _reqRepo....
if (result.HasError)
{
return NotFound(result);
}
return Ok(result);
}
catch (Exception exception)
{
return NotFound(exception.Message);
}
}
when I call it though I don't know what to set as the return object
[HttpPost("[Action]")]
public async Task<IActionResult> SubmitConnectionToBilling(ConnectionModel model)
{
return await ApiCaller.CallPostWebApi(model, **???**,
_billingApiUrl + "/MyMethod");
}
I'm trying a Typed HttpClient as below. I'm looking to find a way to add DefaultRequestHeaders only to my POST request (and not to other requests such GET or PUT). Is there way to achieve this?
Here is my code snippet.
var builder = services
.AddHttpClient("MyService", client =>
{
client.BaseAddress = configuration.BaseAddress;
// Need to default header only for "POST" request
client.DefaultRequestHeaders.Add("MyHeader", "MyHeaderValue");
})
.AddTypedClient<IMyServiceClient, MyServiceRestClient>();
I'm trying to find a way where line client.DefaultRequestHeaders.Add("MyHeader", "MyHeaderValue") is only effective for POST request.
What about a re-usable httpclient that has minimal config and then manage your request by http method specific execution?
For example, consider an async POST method that overrides an internal generic async method that configs the request/response and uses your httpclient to execute. You could pass in the headers you need and/or set the default headers in this method.
public async Task<KeyValuePair<HttpResponseMessage, T>> PostAsync<T>(Uri uri, object data, AuthenticationHeaderValue authHeader = null, Dictionary<string, string> headers = null)
{
return await SendRequestAsync<T, object>(uri.ToString(), data, HttpMethod.Post, authHeader, headers);
}
The internal method is as follows:
private async Task<KeyValuePair<HttpResponseMessage, T>> SendRequestAsync<T, U>(string requestUri, U content, HttpMethod method, AuthenticationHeaderValue authHeader = null, Dictionary<string, string> headers = null)
{
using (HttpRequestMessage request = new HttpRequestMessage())
{
request.Method = method;
request.RequestUri = new Uri(requestUri, UriKind.Absolute);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (authHeader != null)
{
request.Headers.Authorization = authHeader;
}
string requestContent = null;
if (content != null)
{
requestContent = JsonConvert.SerializeObject(content);
request.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
}
if (headers != null)
{
foreach (var header in headers)
{
if (!request.Headers.Contains(header.Key))
{
request.Headers.Add(header.Key, header.Value);
}
}
}
// _client would be a private implementation or injected version of your httpclient
using (HttpResponseMessage response = await _client.SendAsync(request))
{
if (response.IsSuccessStatusCode)
{
if (response.Content != null)
{
var rawJson = await response.Content.ReadAsStringAsync();
var mappedObj = JsonConvert.DeserializeObject<T>(rawJson);
var result = new KeyValuePair<HttpResponseMessage, T>(response, mappedObj);
return result;
}
}
else
{
// do something else
}
return new KeyValuePair<HttpResponseMessage, T>(response, default(T));
}
}
}
I was actually being stupid here. I realized that I could achieve what I wanted through the DelegatingHandler.
var builder = services
.AddHttpClient("MyService", client =>
{
client.BaseAddress = configuration.BaseAddress;
})
.AddHttpMessageHandler<MySpecialHeaderDelegatingHandler>()
.AddTypedClient<IMyServiceClient, MyServiceRestClient>();
public class MySpecialHeaderDelegatingHandler: DelegatingHandler
{
private const string MySpecialHeader = "my-special-header";
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
EnsureMySpecialHeaderExists(request);
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
private static void EnsureMySpecialHeaderExists(HttpRequestMessage request)
{
if (request.Method != HttpMethod.Post) return;
if (!request.Headers.Contains(MySpecialHeader))
{
request.Headers.Add(MySpecialHeader, "MyHeaderValue");
}
}
Previously i handled all my http requests in a single class but i would like to move the http login functionality to a different class but now i cant access the http client response.IsSuccessStatusCode
this is my original code whic works
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl+"/v2auth/default/login");
var response2 = await http.PostAsync(url, credentials);
if (response.IsSuccessStatusCode)
{
//do after login stuff
}
Now i would like to move the login logic to a different class that is in a different folder(auth->dbhelpers)
class LoginHttp
{
public static async Task<object> loginAsync(String username, String password)
{
var values = new Dictionary<string, string>
{
{ "username",username },
{ "password", password }
};
var credentials = new FormUrlEncodedContent(values);
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl + "/v2auth/default/login");
var response = await http.PostAsync(url, credentials);
return response;
}
}
So am now trying to access the returned response via
var responsefromhttplogin = auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin .IsSuccessStatusCode) //this fails
{
//do after login stuff
}
How can i get the retrned response be of type HttpClient again?
Am getting an error of
Task<Objct> does not contain defination for IsSuccessStatusCode
Make your loginAsync method return Task instead. Currently you are returning an object, then you will have access to the IsSuccessStatusCode
class LoginHttp{
public static async Task<HttpResponseMessage> loginAsync(String username, String password)
{
var values = new Dictionary<string, string>
{
{ "username",username },
{ "password", password }
};
var credentials = new FormUrlEncodedContent(values);
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl + "/v2auth/default/login");
var response = await http.PostAsync(url, credentials);
return response;
}
}
You also need to use await in your calling method, otherwise you get a Task back
var responsefromhttplogin = await auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin.IsSuccessStatusCode)
{
//do after login stuff
}
Try to change your code to await async method like this:
var responsefromhttplogin = await auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin.IsSuccessStatusCode)
{
//do after login stuff
}
Now after you add await this responsefromhttplogin.IsSuccessStatusCode should be accessible.
And also change your method to return Task<HttpResponseMessage> instead of Task<object>
You are returning Task<object> from loginAsync(), to access the result of the task you can use Task.Result.
I would consider to narrow down the type that you are returning from object to HttpResponseMessage.
Judging from your code, you are returning a Task<object>
You should either cast the return response; as return (HttpResponseMessage) response;
Or change the return type to be Task<HttpResponseMessage>
Relevant documentation
I'm trying to build an MVC that requests through a PCL to a WebApi. I am sending a get requests and getting stuck on the awaiting for the response. Postman returns the correct values. I also don t get exceptions on send. The 3 projects are both on the same solution.
PCL
HttpResponseMessage httpResponse = null;
try
{
httpResponse = await _http.GetAsync( "http://localhost:43818/api/values" );
}
catch (Exception e)
{
var meessage = e.Message;
var stack = e.StackTrace;
}
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
string json = await httpResponse.Content.ReadAsStringAsync( );
}
So the issue is that in the PCL, it's doesn pass the await, it gets stuck.
MVC
var result = apiClient.GetIndex( );
Web Api
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Also, how do i wait in my MVC for the response before rendering the controller view
In your Class library (PCL), Create method GetIndex as below,
public async Task GetIndexAsync()
{
HttpResponseMessage httpResponse = null;
try
{
_http.BaseAddress = new Uri("http://localhost:43818/");
httpResponse = await _http.GetAsync("api/values");
}
catch (Exception e)
{
var meessage = e.Message;
var stack = e.StackTrace;
}
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
string json = await httpResponse.Content.ReadAsStringAsync();
}
}
And In MVC calling method as below,
var result = apiClient.GetIndexAsync().Wait();
which solved both your problems.
Ok so i found the best sollution. Blocking threads is not a very good idea.
This is the fix
PCL
public async Task<HttpResponseMessage> Register()
{
HttpRequestMessage request = new HttpRequestMessage
{
RequestUri = new Uri( _http.BaseAddress, "account/register/" ),
Method = HttpMethod.Post,
Content = new StringContent( "{\"Email\": \"email#yahoo.com\",\"Password\": \"Password!1\",\"ConfirmPassword\": \"Password!1\"}",
Encoding.UTF8,
_contentType
),
};
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await _http.SendAsync( request, CancellationToken.None );
}
catch (Exception e)
{
Debugger.Break();
}
return response;
}
MVC Client
public async Task<ViewResult> Index( )
{
var thisTask = await Api.Register( );
return View();
}
I have a httpclient that is calling a WebAPI service. The GET reaches the service and returns the content but the client just keeps waiting...
Client code:
static async Task RunAsyncGet(string baseUri, string uri)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUri);
HttpResponseMessage response = await client.GetAsync(uri); // <-- stuck here
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
IEnumerable<UserAccountModel> users = await response.Content.ReadAsAsync<IEnumerable<UserAccountModel>>();
//...
}
}
}
WebAPI code:
public class UserAccountController : ApiController
{
private IRepository _repo;
public UserAccountController(IRepository repo)
{
_repo = repo;
}
public HttpResponseMessage Get()
{
var s = _repo.GetAllUserAccounts();
IContentNegotiator negotiator = Configuration.Services.GetContentNegotiator();
ContentNegotiationResult result = negotiator.Negotiate(typeof(AuthResponseModel), Request, Configuration.Formatters);
var bestMatchFormatter = result.Formatter;
var mediaType = result.MediaType.MediaType;
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new ObjectContent<IQueryable<UserAccount>>(s, bestMatchFormatter, mediaType)
};
}
}
Thoughts?
Further up in your client code (whatever ends up calling RunAsyncGet), some code is calling Task.Wait or Task<T>.Result. That will cause a deadlock if called from the UI thread, as I explain on my blog.
The proper solution is to change that Wait/Result to use await.
This is how I ended up calling the WebAPI:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:23302");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/useraccount").Result;
if (response.IsSuccessStatusCode)
{
var t = response.Content.ReadAsAsync<IEnumerable<UserAccount>>().Result;
...
}
else
{
//Something has gone wrong, handle it here
}
}
It seems that your call to EnsureSuccessStatusCode is the likely culprit. That method actually returns a HttpResponseMessage that will have a HTTP status in the 200 range, or will throw an exception. So, you probably want something like:
static async Task RunAsyncGet(string baseUri, string uri)
{
var client = new HttpClient();
client.BaseAddress = new Uri(baseUri);
HttpResponseMessage response = await client.GetAsync(uri);
IEnumerable<UserAccountModel> users = await response.EnsureSuccessStatusCode().Content.ReadAsAsync<IEnumerable<UserAccountModel>>();
// ... the rest ...
}