I have multiple actions on getting a HighScore that require a single integer id. What would be the common best practice to fix the matched multiple endpoints error?
[HttpGet("{highScoreId}")]
public async Task<IActionResult> GetHighScoreById(int highScoreId)
{
var highScore = await _highScoreRepo.GetHighScoreById(highScoreId);
return Ok(highScore);
}
[HttpGet("{gameId}")]
public async Task<IActionResult> GetHighScoresByGame(int gameId)
{
var highScores = await _highScoreRepo.GetHighScoresByGame(gameId);
return Ok(highScores);
}
You can either use property Name an attribute HttpGetAttribute for each endpoint
[HttpGet("{highScoreId}", Name="GetByHighScoreId")]
public async Task<IActionResult> GetHighScoreById(int highScoreId)
{
var highScore = await _highScoreRepo.GetHighScoreById(highScoreId);
return Ok(highScore);
}
[HttpGet("{gameId}", Name="GetByGameId")]
public async Task<IActionResult> GetHighScoresByGame(int gameId)
{
var highScores = await _highScoreRepo.GetHighScoresByGame(gameId);
return Ok(highScores);
}
or make one endpoint and check the parameters for the absence of a value
[HttpGet()]
public async Task<IActionResult> GetHighScore([FromQuery] int? highScoreId = -1, [FromQuery] int? gameId = -1)
{
if (highScoreId != -1)
{
var highScore = await _highScoreRepo.GetHighScoreById(highScoreId);
return Ok(highScore);
}
if (gameId != -1)
{
var highScores = await _highScoreRepo.GetHighScoresByGame(gameId);
return Ok(highScores);
}
return NotFound();
}
I am setting up a new server, and I want to expose Async API, this API will call a function and this function will call another function and so on, the last function in the calling tree will call an external service and use Async/Await code pattern. what is the right way to implement such API?
Shall I add Async/Await in all functions or just adding them in the last function?
E.g.
[ResponseType(typeof(AnyTypeResponse))]
[HttpPost]
public async Task<IHttpActionResult> MyAPI()
{
var res = await MyFuncTree1();
return Ok(res);
}
public async Task<AnyTypeResponse> MyFuncTree1()
{
var res = await MyFuncTree2();
return res;
}
public async Task<AnyTypeResponse> MyFuncTree2()
{
var res = await MyFuncTree3();
return res;
}
public async Task<AnyTypeResponse> MyFuncTree3()
{
var res = await CallExternalService();
return res;
}
Only the top function needs to be marked async in your example and await the result of MyFuncTree1. The others can just return the result task of the function they are calling (as the result is not used inside the function).
[ResponseType(typeof(AnyTypeResponse))]
[HttpPost]
public async Task<IHttpActionResult> MyAPI()
{
var res = await MyFuncTree1();
return Ok(res);
}
public Task<AnyTypeResponse> MyFuncTree1()
{
return MyFuncTree2();
}
public Task<AnyTypeResponse> MyFuncTree2()
{
return MyFuncTree3();
}
public Task<AnyTypeResponse> MyFuncTree3()
{
return CallExternalService();
}
The client and a generic method for the API requests are created here:
public class Client : IDisposable
{
private HttpClient _client;
private void CreateClient()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_client.DefaultRequestHeaders.Add("KEY", token);
}
public void Dispose() => _client?.Dispose();
public enum Method { GET, POST, PUT, DELETE }
public HttpResponseMessage Request(string url, Method method, object data, HttpContent request)
{
if (data != null)
request = new StringContent(Serialize(data), Encoding.UTF8, "application/json");
switch (method)
{
case Method.POST: return _client.PostAsync(url, request).Result;
case Method.PUT: return _client.PutAsync(url, request).Result;
case Method.DELETE: return _client.DeleteAsync(url).Result;
default: return _client.GetAsync(url).Result;
}
}
public Task<HttpResponseMessage> RequestAsync(string url, Method method, object data, HttpContent request)
{
if (data != null)
request = new StringContent(Serialize(data), Encoding.UTF8, "application/json");
switch (method)
{
case Method.GET: return _client.GetAsync(url);
case Method.POST: return _client.PostAsync(url, request);
case Method.PUT: return _client.PutAsync(url, request);
case Method.DELETE: return _client.DeleteAsync(url);
default: return _client.GetAsync(url);
}
}
public string Post(string url, object data) =>
Request(url, Method.POST, data, null).Content.ReadAsStringAsync().Result;
public Task<HttpResponseMessage> PostAsync(string url, object data) =>
RequestAsync(url, Method.POST, data, null);
//UTILS
private static string Serialize(object data) =>
data == null
? string.Empty
: JsonConvert.SerializeObject(data, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
I'm trying to call these methods to specific classes, to simplify the usage of it for the customer. For example, to create a new checkout for a transaction in a credit card:
public class Checkout : SDK
{
private static Client client;
public Checkout() => client = new Client();
public static async Task Credit(object data) =>
await client.PostAsync(url, data);
}
The request needs to be mounted based on a few models, that can have this structure and I'm trying to generate it in a simple way, like this:
public async Task Test()
{
var transaction = new Transaction
{
PaymentMethod = new PaymentMethod { Code = "1" },
Application = "Test",
Vendor = "Name",
Customer = new Customer
{
//Customer details...
},
Products = new List<TransactionProduct>
{
//Products...
}
};
var teste = Checkout.Credit(transaction);
Console.WriteLine(teste);
}
And all I get as return is:
System.Threading.Tasks.Task`1[System.Threading.Tasks.VoidTaskResult]
Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
I've tried to add await for the Checkout.Credit call, but I get:
CS0815 Test C# Cannot assign void to an implicitly-typed variable
Unit testing this with a simple HttpClient requests works like a charm, but I'm not being able to identify the problem on my project structure, so any help will be very much appreciated.
Task is the return type for an async method that does not have a return value.
Or, to put it another way, async wraps T values into Task<T> (or void returns into Task), and await unwraps those values. Since Credit returns Task, the type of the expression Checkout.Credit(transaction) is Task, and the type of the expression await Checkout.Credit(transaction) is void. And you cannot assign void to var teste; that's what the compiler error is saying.
To fix this, give your async method return types. In particular:
public static async Task<HttpResponseMessage> Credit(object data) =>
await client.PostAsync(url, data);
On a side note, this is quite strange:
public string Post(string url, object data) => ...;
public Task<HttpResponseMessage> PostAsync(string url, object data) => ...;
Usually, if you have a Method and a MethodAsync where Method has some return type TResult, then MethodAsync will have the return type Task<TResult>, not Task<SomeCompletelyDifferentType>. A more natural API would be something like this:
public async Task<HttpResponseMessage> PostAsync(string url, object data)
{
var result = await Request(url, Method.POST, data, null);
return await result.Content.ReadAsStringAsync();
}
I have an issue with a task blocking when I try to retrieve it's result.
I have the following piece of code I want executed synchronously (which is why I'm looking for the result)
I would ignore the reason each call has to be made (legacy software that requires multiple calls through different layers)
the call seems to break down after it starts the task for the final call to be made in the PostCreateProfile, I can see this request never makes it any further than this.
if (CreateProfile(demographics).Result) // Task blocks here
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}
The request will reach its destination if I was to change CreateProfile to an async void like so:
private async void CreateProfile(AppointmentController controller)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(controller);
await profileService.Create(createProfileBindingModel);
}
But I can't return the bool I want to use from this.
Can anyone point out what I am doing wrong?
You should never call .Result on a async/await chain.
Whatever code that calls CreateProfile(demographics) needs to be async too so it can do
if (await CreateProfile(demographics))
{
//dothing
}
Also, if you can you really should put .ConfigureAwait(false) wherever it is logically possible.
if (await CreateProfile(demographics).ConfigureAwait(false)) // depending on what dothing is you may not want it here.
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel).ConfigureAwait(false);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}
I have WebApi controller with async method Post:
public class WebApiController : ApiController
{
public async Task<HttpResponseMessage> Post(HttpRequestMessage request)
{
try
{
string json = await request.Content.ReadAsStringAsync();
var model = JsonConvert.DeserializeObject<TModel>(json);
var newEntity = Mapper.Map<TEntity>(model);
var newEntityVersion = Mapper.Map<TEntityVersion>(model);
var result = await CurrentRepository.AddAsync(newEntity, newEntityVersion);
return CreateResponse(result, "DefaultApiPost");
}
catch (Exception e)
{
Logger.Error("Post in " + ControllerName, e);
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
}
}
}
AddAsync method looks like :
public virtual async Task<TEntity> AddAsync(TEntity entity, TEntityVersion version)
{
await DatabaseContext.SaveChangesAsync();
return entity;
}
When I call Post method using HttpClient I can enter AddAsync method :
client.PostAsync(requestUri, content).Result.Content.ReadAsStringAsync().Result;
But when I call Post method using WebApi controller explicit I can not enter AddAsync method, just jump over it :/ :
var product = FakeProduct();
var newEntity = Mapper.Map<ProductEntity>(product);
var newEntityVersion = Mapper.Map<ProductVersionEntity>(product);
productRepository.AddAsync(newEntity, newEntityVersion).Returns(Task.FromResult<ProductEntity>(newEntity));
var content = JsonConvert.SerializeObject(Product);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:8888/api/Product/")
{
Content = new StringContent(content),
};
var webApiController = FakeWebApiControllerWithPostRoute();
var result = await webApiController.Post(request);
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode);
Any idea why this method is not executed ?