I have a controller with one action. In this action method, I have an async method that I call and that is it. This is the code that I am using:
[HttpGet]
public Task<MyObject> Get()
{
return _task.GetMyObject()
}
This serializes correctly into the JSON I expect from it. Now my manager insists that the signature should be changed to the following:
[HttpGet]
public async Task<IActionResult> Get()
{
var data = await _task.GetMyObject();
return Ok(data);
}
I'm of the belief that there is no reason for the code to await in the controller and can just return the Task because nothing afterwards depends on the result. Apart from the extra code generation (creation of state machine etc.) done for the await, are there any implications from a WebApi point of view of these approaches? To clarify, I want to know if returning an IActionResult is better than to just return Task<MyObject> even tho it seems like the results are the same.
Task< T>
Pro
Unit tests do not require any casting,
Product product = await controller.Get();
Big advantage is, your unit tests become truly independent of underlying HTTP Stack.
Swagger does not need any extra attribute to generate response schema as swagger can easily detect result type.
Another big advantage is, you can reuse your controller in some other controller when the logic remains same.
Also avoiding await before return gives slight improvement in performance as that part of code does not need Task state machine. I think future C# version will omit single await as compiler optimization.
Con
Returning error status code requires throwing exception..
throw new HttpStatusException(404, "File not found");
throw new HttpStatusException(409, "Unauthorized");
Task< IAsyncResult>
Pro
You can return HTTP Status code such as
return NotFound(); // (Status Code = 404)
return Unauthorized(); // (Status Code = 409)
Con
Unit testing requires extra casting..
Product productResult = ((await controller.Get()) as OkResult).Result as Product;
Due to such casting, it becomes difficult to reuse your controllers in some other controller, leading to duplication of logic.
Swagger generator requires extra attribute to generate response schema
[ProducesResponseType(typeof(Product), 200)]
This approach is only recommended when you are dealing with logic that is not part of unit tests, and not part of your business logic such as OAuth integration with third party services where you want to focus more on IActionResult based results such as Challenge, Redirect etc.
Actions can return anything, mostly they return an instance of IActionResult (or Task<IActionResult> for async methods) that produces a response. The action method is responsible for choosing what kind of response it return and the action result does the responding.
If an action returns an IActionResult implementor and the controller inherits from Controller, developers have many helper methods corresponding to many of the choices. Results from actions that return objects that are not IActionResult types will be serialized using the appropriate IOutputFormatter implementation.
For non-trivial actions with multiple return types or options (for example, different HTTP status codes based on the result of operations performed), prefer IActionResult as the return type.
ASP.NET MVC is a conventions over configuration framework. This means any future maintainer of your code, including your future self, will expect code to be written a certain way in order to reduce the number of class files you have to inspect to make changes or additions.
While the result may be the same from your two technically different options, the conventional approach is to async/await your results. Anything other than that convention will potentially cause confusion for future maintainers. Additionally, future releases of MVC may break your code in unknown ways as you did not follow the convention.
Good leadership of software development teams includes instilling a desire to reduce overall manpower needs for the organization by simplifying potential future maintenance of the code. Your manager may be trying to promote this concept.
ASP.NET Core team, while unifying MVC and WEB API (Controller and ApiController), abstracted IActionResult for robust exception handling mechanism.
Throwing exceptions for control flow is an anti-pattern on action methods.
[HttpGet]
public async Task<MyObject> Get()
{
var data = await _task.GetMyObject()
if(data == null)
{
return NotFound(); //Or, return Request.CreateResponse(HttpStatusCode.NotFound)
// Versus, throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
return Ok(data);
}
Note: NotFound(), Ok() etc. are IHttpActionResult things in WEBAPI2 era not new to asp.net core.
Related
C# ASP.NET Core Web API: general newbie question. Trying to implement basic Asynchronous Request-Reply Pattern. Basic idea is that:
Client calls POST {url}/DataProcessor/DataX with some payload
Client checks on the status of the transaction using GET {url}/DataProcessor/DataXStatus, until HTTP Status Code of 200 is returned
Client gets xActionId from the response above and then calls GET {url}/DataProcessor/GetResults/{xActionId} to retrieve results of data processing.
This is what my controller looks like: When I call DataXStatus method (after properly calling the DataX method), the _processData isn't in the right state, like it has gone out of scope after DataX method is done. What am I doing wrong? Is DataProcessorController object gone after any one method is complete? What is the right way to do this?
Thanks in advance
[Route("[controller]")]
[ApiController]
public class DataProcessorController : ControllerBase
{
private ProcessData _processData = new ProcessData() ;
[HttpPost]
[Route("DataX")]
public IActionResult DataX([FromBody] xData value)
{
_processData.CalculateResult(value);
return Accepted();
}
[HttpGet]
[Route("DataXStatus")]
public IActionResult DataXStatus()
{
if(_processData.IsRunning())
{
return Accepted();
}
return Ok(new xDataStatus(){id = _processData.GetxActionId()});
}
[HttpGet]
[Route("GetResults/{xActionId}")]
public IActionResult GetResults(string xActionId)
{
return Ok(new xDataResults(){resX = _processData.GetResults()});
}
}
Answering this on my mobile so please forgive any typos.
Basically, yes the class is re-instaintaited on every request. The api does not keep context of previous requests unless the object you are keeping track of is static, which being me to my first solution for this:
Quick and dirty option:
I would recommend you use dependency injection for this instead. That gives you the option to run your 'proccessData' class as a singleton. Which basically means it's static.
Better option:
The other more correct way is to use a known library for this. There are are tons of libraries that are built for handling async requests in web apis. I think you should use hangfire for this, it takes a few minutes to set up and also has tons of configurion options.
https://docs.hangfire.io/en/latest/getting-started/aspnet-core-applications.html
Let me know if you need any further help!
I'm in a situation where two calls at the same time write to the session (of an asp.net core application running on the old framework), and one of the session variables gets overwritten.
Given the following controller code, assume that the long session gets called first, 200 ms later the short session gets called, and 800 ms later (when the long session is done) the result of both sessions gets called.
[HttpPost("[action]")]
public async Task<IActionResult> TestLongSession() {
HttpContext.Session.SetString("testb", "true");
// If we do this delay BEFORE the session ("testb") is set, then all is fine.
await Task.Delay(1000);
return Ok();
}
[HttpPost("[action]")]
public async Task<IActionResult> TestShortSession() {
HttpContext.Session.SetString("testa", "true");
return Ok();
}
[HttpGet("[action]")]
public async Task<IActionResult> TestResultOfBothSessions() {
string a = HttpContext.Session.GetString("testa");
string b = HttpContext.Session.GetString("testb");
return Ok($"A: {a}, B: {b}");
}
The result of the final call (TestBothSessions) is "A: , B: true".
The question is then: Is there something I missed to make the session work (aka, return "A: true, B: true")?
Obviously, I could remove the delay and all is fine, but in the real application there's a call that potentially can take some time, and I prefer not to write the session variable at a later time (I guess I could with a bit of custom error handling, but then the problem still remains that I no longer trust the asp.net session to work with synchronous calls).
Edit: The typescript code that calls these endpoints from the browser:
this.service.testLongSession().subscribe(() => {
this.service.testBothSessions().subscribe((result: string) => {
console.log(result);
});
});
setTimeout(() => {
this.service.testShortSession().subscribe();
}, 200);
I believe the behavior you observe is what the ASP.NET authors intended. I look at the interfaces that session stores need to implement, namely ISession and ISessionStore, and I see no synchronization mechanisms to prevent the overwriting of data during simultaneous requests.
The benefit of such a simple interface is that it's much easier to implement, and can be easily implemented by a variety of caches and databases.
ASP.NET 4 had a much more complex session store base class SessionStateStoreProviderBase that included locking logic, but it was really challenging to implement.
Im struggling with two ways to return a HttpStatusCode.NotFound (and other Http Status Codes).
1.) My app service will throw a RestException(HttpStatusCode code) which is caught by Middleware and then a proper response is sent back and the response code is set. For example, if I have [Httpget]GetEmployeeById(int Id) and Id is not a real Employee Id, respond with 404.
2.) In my controller, the Application Service call to GetByEmployee() might return null, if so I return NotFound() which does the same as the middleware. But instead, this is done in the controller and the Application Service remains unaware of http status codes.
RestException Pros:
Thinner controllers.
Less complicated code. If I have an action called PutEmployee(Employee employeeToUpdate) I no longer need to write extra logic to fetch first to determine if it exists, pass around tracked entities, etc. The Application Service handles all of it.
Returning Codes in Controller Pros:
Since I am using ActionResult, and I either return an Employee or NotFound() I dont need a [ProducesResponseType(typeof(FluentValidationDataTableFieldError), StatusCodes.Status400BadRequest))] because this can be inferred.
Http/API/Rest stuff stays where it belongs, in the controller.
Essentially, its the difference between the following snippets:
[HttpPost("", Name = EmployeeControllerRoute.PostEmployee)]
[ProducesResponseType(typeof(EmployeeCreateUpdateJsonModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SpecialErrorViewModel), StatusCodes.Status400BadRequest)]
public async Task<EmployeePageJsonViewModel> Post([FromBody]EmployeeCreateUpdateJsonModel employeeToAdd)
=> await _employeeService.AddAsync(employeeToAdd); // AddAsync throws `RestException(HttpStatusCode code)` if `employeeToAdd.Id` does not pull up a real item.
VS
[HttpGet("{productIds}", Name = ProductControllerRoute.GetProducts)]
public async Task<ActionResult<List<ProductViewModel>>> Get(EntityIdList productIds)
{
var products = await _productService.GetMultipleByIdAsync(productIds);
if(products.Count != productIds.Count)
{
return NotFound();
}
return products;
}
I am trying to implement Google SSO in my C# web application. It seemed to be pretty straightforward. Based on this tutorial, Google does its magic in the web browser to get an Id_Token JWT. I pass the token is passed it to my web service for validation and use it to match it to a user in the application.
Although I am not sure if my approach with the server-side Google API is correct yet, my big hangup is trying to figure out how to work with the async call in the Google API to see what I get back.
My code is pretty simple using the Google.Apis.Auth namespace:
public async Task<GoogleJsonWebSignature.Payload> wfValidateAsync(string pId_Token)
{
GoogleJsonWebSignature.Payload Sig = await GoogleJsonWebSignature.ValidateAsync(pId_Token, null, false);
return Sig;
}
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks. My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack. I think it means ending the service call before the async response finishes and the service can act on the result.
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
Thanks in advance to anyone who can help me screw my head back on straight with this.
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks.
But not use Typescript, right?
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
They should. Bad things can happen if you don't.
I think it means ending the service call before the async response finishes and the service can act on the result.
No! The compiler generates a state machine for methods with the async modifier and the await keyword means "go do something else and I'll come back here when I'm done".
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
You're probably making your test methods async void. They should be async Task in order for the test engine to know when the test is done.
Have a look at Stephen Cleary's blog. He has lots of content on async-await.
Paulo, Thank you!!
I was able to get this working with your advice on the last part about the test method. I had to change this:
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER
[TestMethod]
public async void AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
To this:
//THIS TEST METHOD SHOWS IN THE TEST EXPLORER
[TestMethod]
public async Task AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
NOW -- as an additional gotcha that was hanging me up, this will also cause a unit test to disappear from the test explorer, which I found out through lazy copy/pasting a non-test method's definition and mindlessly just adding a return when the build output told me I needed to return a value.
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER DUE TO RETURN VALUE
[TestMethod]
public async Task<bool> AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
return true;
}
In addition to the excellent blog you shared, this article from MSDN Magazine entitled Async Programming : Unit Testing Asynchronous Code helped me get my brain around it too.
What was hanging me up with all of this was mixing synchronous and async methods which, to your point, was not working well. The code seemed to skip the debug points I had set after the await calls as if it never ran them or, if it did run them, ran them somewhere I could not see and couldn't log to the debugger.
It was one of those terrible moments late on a Friday night where a developer starts to question both competence and sanity! :)
Thanks again for the help!
p.s. I incorrectly typed Angular JS in my question and meant Angular 4...and I am using TypeScript there. Some day I'll stop incorrectly referring the newer versions of Angular as AngularJS.
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
It's not entirely true. You can use async method in sync methods. Of course you are losing most of the 'async' effect, but sometimes you have to. As async methods returs tasks you have some options.
When Task is returning result, any reference to t.Result will block execution till it's known. Another option is to use Task.Wait(preferably with timeout). For example code to validate google jwt token:
public bool ValidateGoogleToken(string token)
{
try
{
if(GoogleJsonWebSignature.ValidateAsync(token).Wait(1000))
return true; //success
//timeout exceeded
}
catch (Exception e)
{
//tampered token
}
return false;
}
I am working on a Travel related website and here is my high level architecture:
ASP.NET MVC 2 (Presentation Layer)
Services Layer
Repository Layer communicating with external J2EE Services
Currently all of my layers support ONLY Synchronous communication like this:
//Pricing Controller...
public ActionResult GetRates()
{
//Contruct Pricing Request
//PricingRequest request = Contruct Pricing Request...;
PricingResponse response = _pricingService.GetComponentPricing(request);
//....
//...
return View(response);
}
//Pricing Service...
public PricingResponse GetComponentPricing(PricingRequest pricingRequest)
{
//Do Something...
PricingResponseDomainObject responseDomainObject = _pricingRepository.GetComponentPricing(pricingRequest.ConvertToPricingRequestDomainObject());
//Apply Business Rules and perform some action on response
//Convert DomainObjectResponse into ServiceObjectResponse...
return response(PricingResponse);
}
//Pricing Repository...
public PricingResponseDomainObject GetComponentPricing(PricingRequestDomainObject pricingRequest)
{
//Do Something...
//Call J2EE Service and Get Response
//Return Response
return response(PricingResponseDomainObject);
}
Now I have a need to change my Pricing Controller to AsyncController and convert public ActionResult GetRates() into Asynchronous Action.
What changes do I need to do in my Service & Repository layers in order to support Asynchronous operations? Do I have to re-write them completely?
Edit: Need for changing Controller into Asynchronous
For a particular Search scenario, I need to call the J2EE service 1 to 5 times (independent operations), consolidate all responses into a single response and hand it over to the Controller to be able to present it to user.
No, you likely will not have to do anything. In fact, converting them to async themselves would make your code a lot more complicated. The idea behind an async controller is that the controller action returns when it's done processing, and since those methods will still be syncronous that works out just fine. You do your work, and return when you're finished.