I am having a problem with getting a response from an API with my code, the request does not time out and it does not give me a response at all. I have made an api endpoint in my own API to return the json string to then manually post the json data with "Firefox Poster" and it works just fine. With this I believe that the problem is somewhere in my code.
I got a C# WebAPI which I am developing to be used with an Angular frontend(This works, it's just for history). When calling my API I create the object "EnrollmentRequestDto"
public class EnrollmentRequestDto
{
/// <summary>
/// Context Information for the request. Contains the Ship-To, language code and timezone.
/// </summary>
[JsonProperty("requestContext")]
public RequestContextDto RequestContext { get; set; }
/// <summary>
/// Unique ID provided by DEP to reseller on provisioning DEP access. This would be the reseller's DEP ID if its posted by distributor OBO a reseller, and would be his own depResellerId if a reseller is posting for self.
/// </summary>
[JsonProperty("depResellerId")]
public string DepResellerId { get; set; }
/// <summary>
/// Unique transaction ID provided by the reseller
/// </summary>
[JsonProperty("transactionId")]
public string TransactionId { get; set; }
/// <summary>
/// List of orders in the transaction (limit 1000 per transaction)
/// </summary>
[JsonProperty("orders")]
public List<OrderDto> Orders { get; set; }
}
After this object is created I send to to my class RequestHandler and the method BulkEnrollRequest, which atm is written with HttpClient extension Flurl, which can be found here: github
public IResponse BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
try
{
var result = _bulkEnrollUrl.PostJsonAsync(enrollmentRequest).ReceiveJson<SuccessResponse>();
result.Wait();
return result.Result;
}
catch (FlurlHttpTimeoutException)
{
throw new AppleTimeOutException();
}
catch (FlurlHttpException ex)
{
return _errorHandler.DeserializeFlurlException(ex);
}
}
I have also tried this to make sure that nothing happens in Flurl(This is just to debug to the point where I want to get the response):
public async Task<IResponse> BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
var json = JsonConvert.SerializeObject(enrollmentRequest);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.PostAsync(_bulkEnrollUrl, new StringContent(json, Encoding.UTF8, "application/json"));
return new SuccessResponse();
}
And the code freezes at the await point and nothing is happening. I have tried to place a breakpoint after the BulkEnrollRequest so it never leaves this method.
Now here's for the funny part: It did work while I was working on the ErrorHandler and at some point the API just stopped responding. Wireshark shows that a request is being made... so I am stuck.
Any help is appreciated.
EDIT
This now works! I implemented async all the way from the API Controller down to RequestHandler, then awaited for every call. For reference:
public async Task<IResponse> BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
try
{
var result = await _bulkEnrollUrl.PostJsonAsync(enrollmentRequest).ReceiveJson<SuccessResponse>();
return result;
}
catch (FlurlHttpTimeoutException)
{
throw new AppleTimeOutException();
}
catch (FlurlHttpException ex)
{
return _errorHandler.DeserializeFlurlException(ex);
}
}
This is now fixed. I believe that the reason why the Wireshark traffic looked so weird was because of a deadlock in the code, and the timeout was on my side which meant that I could never FIN ACK the information. To fix this i implemented async on all methods, from the Controller down to my RequestHandler class. For reference:
public async Task<IResponse> BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
try
{
var result = await _bulkEnrollUrl.PostJsonAsync(enrollmentRequest).ReceiveJson<SuccessResponse>();
return result;
}
catch (FlurlHttpTimeoutException)
{
throw new AppleTimeOutException();
}
catch (FlurlHttpException ex)
{
return _errorHandler.DeserializeFlurlException(ex);
}
}
Try this code
Install-Package Microsoft.AspNet.WebApi.Client
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("api/products", enrollmentRequest);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<T>();
}
}
Related
I have a scenario where I need to respond to a request that it's been received and send a response (request?) to another endpoint once internal api calls and logic has completed. The flow looks like this:
External request to my endpoint > endpoint responds to request with accepted > endpoint passes the request on internally > internal logic fetches and handles data from DB > internal logic uses data from DB to send a request back to a different endpoint from the same integration as the first call came from.
I have managed to get it to work using Queued Background Tasks to send the request to the correct internal handler with Mediatr. However in order for it to work I need to add the barer token from the request header to the request object and then use that barer token to validate against the internal API's. I'd like to avoid this since I might run into the issue of the token expiring or not being valid for the internal Api etc.
Request object example:
public class ExampleRequest : IRequest, IRequest<ExampleResponse>
{
public string? Token { get; set; } //Added for functioning version, want to get rid
//of it
public CommonData Data { get; set; }
public string RequestId { get; set; }
public string OperationId { get; set; }
public List<string> ObjectIdentifiers { get; set; }
}
public class CommonData
{
public string MessageId { get; set; }
public DateTime Timestamp { get; set; }
}
Response object example (response to the call):
public class ExampleResponseForCall
{
public CommonData Data { get; set; }
public string ResponseStatus { get; set; } //Will be accepted/acknowledged
}
Example response object (for final response)
public class ExampleResponse
{
public CommonData Data{ get; set; }
public string ResponseStatus { get; set; }
public string ErrorCode { get; set; }
public string ErrorDescription { get; set; }
public string RequestId { get; set; }
public string OperationId { get; set; }
}
My current working version looks something like this:
**Endpoint:**
public IActionResult Post(ExampleRequest request)
{
var authorization = Request.Headers[HeaderNames.Authorization];
if (AuthenticationHeaderValue.TryParse(authorization, out var headerValue))
{
var scheme = headerValue.Scheme;
var parameter = headerValue.Parameter;
}
var token = headerValue?.Parameter;
request.Token = token; //I added token as a nullable string on the request obj
_backgroundTaskQueue.StartTask(request);
return Ok(new ExampleResponseForCall
{
Data = request.Data,
ResponseStatus = HttpStatusCode.Accepted.ToString()
});
}
**Background Task queue:**
public void StartTask(IRequest request)
{
_logger.LogInformation("Task is starting.");
_request = request;
Task.Run(async () => await AddTaskAsync(), _cancellationToken);
}
private async ValueTask AddTaskAsync()
{
await _taskQueue.QueueBackgroundWorkItemAsync(BuildWorkItem);
}
private async ValueTask BuildWorkItem(CancellationToken token)
{
var guid = Guid.NewGuid().ToString();
_logger.LogInformation("Task {Guid} is starting.", guid);
if (_request == null)
{
_logger.LogWarning("Request for task {Guid} is null.", guid);
return;
}
await _mediator.Send(_request, token);
_logger.LogInformation("Task {Guid} is complete.", guid);
}
I also have Handlers that can handle the request and Clients for sending requests internally and back to the caller. All of that works when awaiting the internal logic to be handled. However when I'm using the background task queue the internal client fails on the when getting the token here
protected async Task<HttpClient> CreateBaseClient()
{
var client = _clientFactory.CreateClient(HttpClientName);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept",
$"application/json");
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", await GetToken());
return client;
}
public async Task<string> GetToken()
{
if (_httpContextAccessor.HttpContext == null)
throw new Exception("No HttpContext available when trying to
get Token.");
_httpContextAccessor.HttpContext.Items.TryGetValue(Constants.AuthenticationSchemeKey,
out var scheme);
if (scheme?.ToString() == Constants.Bearer)
return GetTokenFromRequest();
throw new MissingAccessTokenException("Unknown authentication type");
}
My workaround (that I want to get away from) looks like this:
protected async Task<HttpClient> CreateBaseClient(string version, string token)
{
var client = _clientFactory.CreateClient(HttpClientName);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept",
$"application/json");
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", token); //token from requst.Token
return client;
}
I've tried to pass in the a lot of different things to the Background Task queue (and changing the parameter type to match ofc) but nothing works. I want to have the background task queue generic since I'll be implementing this for other end points as well.
That's a lot of text so TL:DR, I respond to a request but need to use the token from the request for other calls after responding.
We decided to go with the working solution provided in the question itself.
Due to how our infrastructure is set up we won't be able to get a refresh token (as suggested by #GHDevOps and #TheWallrus) since we won't be able to get the login/id and password/secret of the user in a safe and reasonable way.
However, the working solution in the question has some drawback which should be analyzed on a case-to-case basis. We know that the Api sending us the requests will fetch a new (relevant) token approximately 10 minutes before the current (relevant) token expires and use the new token for all coming requests. Since the logic we apply before passing on the request to our backend is very simple (just simple remapping) we should rarely run into issues with the token expiring before the request has been sent, and in the rare cases that is has, we will send that information in the request back to the external Api, giving them a chance to resend the original request. If the external Api isn't fetching a new token before the expiration of the current token that might cause the token to expire before reaching the internal Api more often which might be a good thing to look after if you're implementing a similar solution.
The code changes that I made for this to function are just minor refactoring (see below). Hope this help anyone else running into a similar issue!
//Endpoint
public IActionResult Post(ExampleRequest request)//Before
{
var authorization = Request.Headers[HeaderNames.Authorization];
if (AuthenticationHeaderValue.TryParse(authorization, out var headerValue))
{
var scheme = headerValue.Scheme;
var parameter = headerValue.Parameter;
}
var token = headerValue?.Parameter;
request.Token = token; //I added token as a nullable string on the request obj
_backgroundTaskQueue.StartTask(request);
return Ok(new ExampleResponseForCall
{
Data = request.Data,
ResponseStatus = HttpStatusCode.Accepted.ToString()
});
}
public IActionResult Post(ExampleRequest request)
{
request.Token = GetToken(Request);//Made into a separate function in the inherited class
_backgroundTaskQueue.StartTask(request);
return Ok(new ExampleResponseForCall
{
Data = request.Data,
ResponseStatus = HttpStatusCode.Accepted.ToString()
});
}
protected string GetToken(HttpRequest request)
{
var authorization = request.Headers[HeaderNames.Authorization];
_ = AuthenticationHeaderValue.TryParse(authorization, out var headerValue);
if (headerValue == null)
{
return "";
}
return string.Equals(headerValue.Scheme, "Bearer", StringComparison.InvariantCultureIgnoreCase) ?
headerValue.Parameter : "";
}
//Client
protected async Task<HttpClient> CreateBaseClient()//before
{
var client = _clientFactory.CreateClient(HttpClientName);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept",
$"application/json");
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", await GetToken());
return client;
}
protected async Task<HttpClient> CreateBaseClient(string token = "")
{
var client = _clientFactory.CreateClient(HttpClientName);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", $"application/json");
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", string.IsNullOrEmpty(token) ?
await GetToken() : token); //We will only send in a token if we are async
return client;
}
I have the following in my controller:
public async Task<IActionResult> IndexAsync()
{
string baseUrl = "https://apilink.com";
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(baseUrl))
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
if (data != null)
{
var recipeList = JsonConvert.DeserializeObject<Recipe[]>(data);
return View();
}
}
return View();
}
I want to unit test this but cannot work out how to test the HttpClient.
I have tried:
[Test]
public void Index_OnPageLoad_AllRecipesLoaded()
{
var testController = new HomeController();
mockHttpClient.Setup(m => m.GetAsync(It.IsAny<string>())).Returns(
() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
mockHttpContent.Setup(m => m.ReadAsStringAsync()).Returns(() => Task.FromResult(LoadJson()));
var result = testController.IndexAsync();
Assert.IsNotNull(result);
}
// Loads the Json data as I don't actually want to make the API call in the test.
public string LoadJson()
{
using (StreamReader r = new StreamReader("testJsonData.json"))
{
string json = r.ReadToEnd();
return json;
}
}
Is there a way to mock this effectively/simply? Or should I maybe inject my own IHttpClient interface? (I am not sure if that is good practice?)
Thanks
There are several ways to unit test HttpClient, but none are straightforward because HttpClient does not implement a straightforward abstraction.
1) Write an abstraction
Here is a straightforward abstraction and you can use this instead of HttpClient. This is my recommended approach. You can inject this into your services and mock the abstraction with Moq as you have done above.
public interface IClient
{
/// <summary>
/// Sends a strongly typed request to the server and waits for a strongly typed response
/// </summary>
/// <typeparam name="TResponseBody">The expected type of the response body</typeparam>
/// <param name="request">The request that will be translated to a http request</param>
/// <returns>The response as the strong type specified by TResponseBody /></returns>
/// <typeparam name="TRequestBody"></typeparam>
Task<Response<TResponseBody>> SendAsync<TResponseBody, TRequestBody>(IRequest<TRequestBody> request);
/// <summary>
/// Default headers to be sent with http requests
/// </summary>
IHeadersCollection DefaultRequestHeaders { get; }
/// <summary>
/// Base Uri for the client. Any resources specified on requests will be relative to this.
/// </summary>
AbsoluteUrl BaseUri { get; }
}
Full code reference here. The Client class implements the abstraction.
2) Create a Fake Http Server and Verify the Calls on the Server-Side
This code sets up a fake server, and your tests can verify the Http calls.
using var server = ServerExtensions
.GetLocalhostAddress()
.GetSingleRequestServer(async (context) =>
{
Assert.AreEqual("seg1/", context?.Request?.Url?.Segments?[1]);
Assert.AreEqual("seg2", context?.Request?.Url?.Segments?[2]);
Assert.AreEqual("?Id=1", context?.Request?.Url?.Query);
Assert.AreEqual(headerValue, context?.Request?.Headers[headerKey]);
if (hasRequestBody)
{
var length = context?.Request?.ContentLength64;
if (!length.HasValue) throw new InvalidOperationException();
var buffer = new byte[length.Value];
_ = (context?.Request?.InputStream.ReadAsync(buffer, 0, (int)length.Value));
Assert.AreEqual(requestJson, Encoding.UTF8.GetString(buffer));
}
if (context == null) throw new InvalidOperationException();
await context.WriteContentAndCloseAsync(responseJson).ConfigureAwait(false);
});
Full code reference here.
3) Mock the HttpHandler
You can inject a Mock HttpHandler into the HttpClient. Here is an example:
private static void GetHttpClientMoq(out Mock<HttpMessageHandler> handlerMock, out HttpClient httpClient, HttpResponseMessage value)
{
handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(value)
.Verifiable();
httpClient = new HttpClient(handlerMock.Object);
}
Full code reference. You can then verify the calls from the mock itself.
I have created web api and calling service method from MVC application. I have implemented authentication in Web Api.
I have created Generic type method from which I call my service method and returning result to action method like below:
public async Task < T > GetWSObject < T > (string uriActionString)
{
T returnValue = default (T);
try
{
using(var client = new HttpClient())
{
//client.BaseAddress = new Uri("http://localhost:50524/");
//client.DefaultRequestHeaders.Accept.Clear();
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HttpClient cons = new HttpClient();
client.BaseAddress = new Uri("http://localhost:50524/"); // Web Service URL
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var data = Encoding.ASCII.GetBytes("Ankita:ankita12");
var header = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(data));
client.DefaultRequestHeaders.Authorization = header;
HttpResponseMessage response = await client.GetAsync(uriActionString);
response.EnsureSuccessStatusCode();
returnValue = JsonConvert.DeserializeObject < T > (((HttpResponseMessage) response).Content.ReadAsStringAsync().Result);
}
return returnValue;
}
catch (Exception e)
{
throw (e);
}
}
Moreover, I am calling this method from my action method like:
public async Task<ActionResult> ViewAuthor(Author author)
{
ViewBag.Message = "Your Author page.";
Author authors = new Author();
string urlAction = String.Format("api/Authors/GetAuthor/{0}", author.Id);
authors = await GetWSObject<Author>(urlAction);
return View(authors);
}
Now how can I return message from catch block in case of Unauthorized access as GetWSObject method is having generic type.
I just want to display message on my Author detail page like you are unauthorized to perform this action. How can I do it?
You have two choices
Add a type to wrap the result for example WSResult<T>. Then your method will return that type. It can look like this
public class WSResult
{
public T Result { get; set; }
public HttpStatusCode StatusCode { get; set; }
}
Then your method will return this type and consumers will check the status first. This is similar to how HttpResponseMessage works. You can copy ideas from it.
In this case your method has a signature like this
public async Task<WSResult<T>> GetWSObject<T> (string uriActionString)
You return things from it like this
return new WSResult<T> { Result = returnValue, Status = HttpStatusCode.OK }
Or in the catch
return new WSResult<T> { Result = returnValue, Status = HttpStatusCode.InternalServerError }
Note that the actual status code in the exception may differ and you should catch the proper exception. You can also use a boolean instead of HttpStatusCode and call it IsSuccessful or something. You can also roll your own enum with values suitable for your code.
Alternatively you can throw an exception that contains the status code. The second way is easier to implement and requires less refactoring but the first one is the recommended way.
My Question: How do I do this?
So, I hadn't touched anything .Net in about 6 years until this week. There's a lot that I've forgotten and even more that I never knew and while I love the idea of the async/await keywords, I'm having a slight problem implementing the following requirements for a client's API implementation:
The ServerAPI class has a method for each of the API methods, taking appropriate input parameters (e.g. the method Login takes in an id and a password, makes the API call and returns the result to the caller).
I want to abstract away the JSON so that my API methods return the actual object you're fetching (e.g. the Login method above returns a User object with your auth token, uid, etc.)
Some API methods return a 204 on success or no meaningful content (not meaningful in my usecase maybe I only care about success/failure), for these I'd like to return either a bool (true = success) or the status code.
I'd like to keep the async/await (or equivalent) design, because it seems to really work well so far.
For some methods, I might need to just return the HttpResponseMessage object and let the caller deal with it.
This is roughly what I have so far and I'm not sure how to make it compliant with the above OR whether I'm even doing this right. Any guidance is appreciated (flaming, however, is not).
// 200 (+User JSON) = success, otherwise APIError JSON
internal async Task<User> Login (string id, string password)
{
LoginPayload payload = new LoginPayload() { LoginId = id, Password = password};
var request = NewRequest(HttpMethod.Post, "login");
JsonPayload<LoginPayload>(payload, ref request);
return await Execute<Account>(request, false);
}
// 204: success, anything else failure
internal async Task<Boolean> LogOut ()
{
return await Execute<Boolean>(NewRequest(HttpMethod.Delete, "login"), true);
}
internal async Task<HttpResponseMessage> GetRawResponse ()
{
return await Execute<HttpResponseMessage>(NewRequest(HttpMethod.Get, "raw/something"), true);
}
internal async Task<Int32> GetMeStatusCode ()
{
return await Execute<Int32>(NewRequest(HttpMethod.Get, "some/intstatus"), true);
}
private async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate)
{
if (authenticate)
AuthenticateRequest(ref request); // add auth token to request
var tcs = new TaskCompletionSource<RESULT>();
var response = await client.SendAsync(request);
// TODO: If the RESULT is just HTTPResponseMessage, the rest is unnecessary
if (response.IsSuccessStatusCode)
{
try
{
// TryParse needs to handle Boolean differently than other types
RESULT result = await TryParse<RESULT>(response);
tcs.SetResult(result);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
else
{
try
{
APIError error = await TryParse<APIError>(response);
tcs.SetException(new APIException(error));
}
catch (Exception e)
{
tcs.SetException(new APIException("Unknown error"));
}
}
return tcs.Task.Result;
}
This is the APIError JSON structure (it's the status code + a custom error code).
{
"status": 404,
"code":216,
"msg":"User not found"
}
I would prefer to stay with System.Net, but that's mostly because I don't want to switch all my code over. If what I want is easier done in other ways then it's obviously worth the extra work.
Thanks.
Here is an example of how I've done it using MVC API 2 as backend. My backend returns a json result if the credentials are correct. UserCredentials class is the exact same model as the json result. You will have to use System.Net.Http.Formatting which can be found in the Microsoft.AspNet.WebApi.Client NugetPackage
public static async Task<UserCredentials> Login(string username, string password)
{
string baseAddress = "127.0.0.1/";
HttpClient client = new HttpClient();
var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("xyz:secretKey"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader);
var form = new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", username },
{ "password", password },
};
var Response = await client.PostAsync(baseAddress + "oauth/token", new FormUrlEncodedContent(form));
if (Response.StatusCode == HttpStatusCode.OK)
{
return await Response.Content.ReadAsAsync<UserCredentials>(new[] { new JsonMediaTypeFormatter() });
}
else
{
return null;
}
}
and you also need Newtonsoft.Json package.
public class UserCredentials
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
//more properties...
}
i would use a Deserializer.
HttpResponseMessage response = await client.GetAsync("your http here");
var responseString = await response.Content.ReadAsStringAsync();
[Your Class] object= JsonConvert.DeserializeObject<[Your Class]>(responseString.Body.ToString());
So, first to address the you need Newtonsoft.Json comments, I really haven't felt the need yet. I've found the built in support to work well so far (using the APIError Json in my original question:
[DataContract]
internal class APIError
{
[DataMember (Name = "status")]
public int StatusCode { get; set; }
[DataMember (Name = "code")]
public int ErrorCode { get; set; }
}
I have also defined a JsonHelper class to (de)serialize:
public class JsonHelper
{
public static T fromJson<T> (string json)
{
var bytes = Encoding.Unicode.GetBytes (json);
using (MemoryStream mst = new MemoryStream(bytes))
{
var serializer = new DataContractJsonSerializer (typeof (T));
return (T)serializer.ReadObject (mst);
}
}
public static string toJson (object instance)
{
using (MemoryStream mst = new MemoryStream())
{
var serializer = new DataContractJsonSerializer (instance.GetType());
serializer.WriteObject (mst, instance);
mst.Position = 0;
using (StreamReader r = new StreamReader(mst))
{
return r.ReadToEnd();
}
}
}
}
The above bits I already had working. As for a single method that would handle each request execution based on the type of result expected while it makes it easier to change how I handle things (like errors, etc), it also adds to the complexity and thus readability of my code. I ended up creating separate methods (all variants of the Execute method in the original question:
// execute and return response.StatusCode
private static async Task<HttpStatusCode> ExecuteForStatusCode (HttpRequestMessage request, bool authenticate = true)
// execute and return response without processing
private static async Task<HttpResponseMessage> ExecuteForRawResponse(HttpRequestMessage request, bool authenticate = true)
// execute and return response.IsSuccessStatusCode
private static async Task<Boolean> ExecuteForBoolean (HttpRequestMessage request, bool authenticate = true)
// execute and extract JSON payload from response content and convert to RESULT
private static async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate = true)
I can move the unauthorized responses (which my current code isn't handling right now anyway) into a new method CheckResponse that will (for example) log the user out if a 401 is received.
I have a windows service which has the following code
public class CertificationStatusCheckJob : IJob
{
public readonly ILog _logger = LogManager.GetCurrentClassLogger();
readonly HttpClient client = new HttpClient { BaseAddress = new Uri(ConfigurationManager.AppSettings["MercuryServicesWebApiUrl"]) };
// Temporary local versoin of statuses
private enum CertificationStatus
{
Active = 1,
Pending,
PendingRenewal,
RenewalPastDue,
ReinstatementOnCurrentCycle,
ClosedInactive,
ClosedRenewed
};
public async void Execute(IJobExecutionContext context)
{
Dictionary<string, int> designationWindows = new Dictionary<string, int>
{
{"RenewalWindow", 10},
{"CertificationLength", 12},
{"FreeGrace", 1},
{"InactiveAccessLength",1},
{"InactivityLength", 36}
};
Console.WriteLine("CertificationApplicationStatusCheckJob: Creating Cert application status job");
string content = null;
List<Certification> retrievedCerts = null;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Call webapi to retrieve all applications
var certificationsResponse = client.GetAsync("certifications/getAllCertifications").Result;
// loop through all applications and compare to rules
if (certificationsResponse.IsSuccessStatusCode)
{
content = certificationsResponse.Content.ReadAsStringAsync().Result;
Console.WriteLine(content);
}
if (content != null)
{
retrievedCerts = JsonConvert.DeserializeObject<List<Certification>>(content);
_logger.Debug("Got Certifications OK");
}
// Allows for all task calls to service api to be performed in parallel in async
if (retrievedCerts != null)
await Task.WhenAll(retrievedCerts.Select(i => CheckCert(i)) as IEnumerable<Task>);
}
private async Task<object> CheckCert(Certification cert)
{
// current date time to compare the range for each state below to.
// if this date falls in the range for the state, do not change the state,
// otherwise kick them into the next state.
DateTime currentDateTime = DateTime.UtcNow;
var newCertStatus = new { certUniqueId = Guid.NewGuid(), statusId=6 };
switch ((CertificationStatus)cert.CertificationStatusId)
{
case CertificationStatus.Active:
//Condition to test for when the cert is in the active state
await client.PostAsJsonAsync("certifications/updateStateForCertification", newCertStatus);
break;
case CertificationStatus.Pending:
break;
case CertificationStatus.PendingRenewal:
break;
case CertificationStatus.RenewalPastDue:
break;
case CertificationStatus.ReinstatementOnCurrentCycle:
break;
case CertificationStatus.ClosedInactive:
break;
case CertificationStatus.ClosedRenewed:
break;
}
return null;
}
}
The Following are the services that are getting called
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Gets all certifications. </summary>
/// <returns> all certifications. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////
[Route("getAllCertifications")]
[AllowAnonymous]
public async Task<List<Certification>> GetAllCertifications()
{
List<Certification> certList = null;
try
{
certList = await _certificationService.GetAllCertifications();
}
catch (Exception e)
{
_logger.Error("exception GetAllCertifications", e);
throw;
}
return certList;
}
//TODO WRITE SERVICE ENTRY POINT FOR SAVE ROUTINE
[Route("updateStateForCertification")]
[AcceptVerbs("POST")]
[AllowAnonymous]
public void UpdateStateForCertification(Guid certUniqueId, int statusId)
{
List<Certification> certList = null;
try
{
_certificationService.UpdateStateForCertification(certUniqueId, statusId);
}
catch (Exception e)
{
_logger.Error("exception UpdateStateForCertification", e);
throw;
}
}
}
I've verified the GetAsync GetAllCertifications() call works as I can debug into that code block. However when I do the PostAsJsonAsync using the anonymous type it won't work. I know json only cares about the properties. I've also verified that it DOES hit the PostAsJsonAsync line of code, so it should be performing the post. So what am I doing wrong?
the problem is your web api method. Your post method accepts two "simple" types. And per default simple types are read from the URI NOT from the body.
The body is read for a complex type.
So you have two options:
Make the parameter in your web api method a complex type with appropriate properties
Put your parameters in your URI to get them
You can't read two primitive types from the body, you can only force one to be read with [FromBody] Attribute.
In more detail you can read the parameter binding here: Parameter Binding in ASP.NET Web API