The recommended way to make an edit page for ASP.NET MVC is to have two methods on a controller called Edit: one GET action and one POST action, both sharing the same name but overloaded differently. Validation errors are shown on the POST action if the edit fails. Then the user can share or bookmark the URL even if it's off of a POST: the URL goes to the GET version on the return.
So far, so good. But then there's the ASP.NET async pattern on controllers. You have EditAsync and EditCompleted. On the two different EditCompleted methods, how do you tell the GET apart from the POST? If you rename the POST action, you lose the nice behavior discussed earlier.
Is there a nice way to get these two patterns to work together?
Generally the XyzAsync() method provides the XyzCompleted() method some state object that tells it what unit of work is being performed, so the XyzCompleted() method can inspect this object and do the right thing. However, if you want to have a different Completed method for each verb, this is possible via the following:
[ActionName("Edit"), HttpGet]
public void EditGetAsync() { }
public ActionResult EditGetCompleted() { }
[ActionName("Edit"), HttpPost]
public void EditPostAsync() { }
public ActionResult EditPostCompleted() { }
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 am working with a Asp.Net Core Web API and I'm having an issue with one of my static object properties only being set on the second call to the controller.
I have some code that executes before the controller action via a attribute [ValidateAuthToken] that I created. The code for that looks something like this
public async override void OnActionExecuting(ActionExecutingContext filterContext)
{
//If our token is valid, no need to do anything.
if (!TokenStorage.AuthToken.IsTokenExpired())
{
base.OnActionExecuting(filterContext);
}else{
TokenStorage.AuthToken.Token = service.GetToken();
}
On my controller I have something like this
public async Task<IActionResult> Contents([FromBody] Model request)
{
var result = service.GetPageData(request.PageName, TokenStorage.AuthToken.TokenType, TokenStorage.AuthToken.Token);
return Ok();
}
AuthToken is a static object on the TokenStorage class.
From debugging it seems like null values are being passed into the service call in the controller, the first time around. The second time around it seems like those values are there. I checked that the values are getting generated on and set to the static object before it hits the controller.
From doing some reading it seems like this is a threading issue, but I'm not knowledgeable enough on threading or threading in the ASP.NET Core environment to come up with a solution and Google has turned up results that don't really fit in my situation.
Any insight would be great.
I have a C# web api project, basically I need to log everytime a method is getting called (ie. method name, parameters passed, result, stacktrace (if error occured), etc).
Right now, I did this by adding few lines to every method and it seems bloated, is there more efficient way to achieve this?
Any help will be appreciated.
Thank you
Middleware is one approach, or you can also create a Filter that lets you perform 'before' and 'after' operations,
https://www.tutorialsteacher.com/webapi/web-api-filters
[Code from the above link]
public class LogDemoAttribute : Attribute, IActionFilter
{
public LogDemoAttribute()
{
}
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
Trace.WriteLine(string.Format("Action Method {0} executing at {1}", actionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
var result = await continuation();
Trace.WriteLine(string.Format("Action Method {0} executed at {1}", actionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
return result;
}
public bool AllowMultiple
{
get { return true; }
}
}
This does let you add to the Global Filters (in which case, you might as well use a Middleware), or add it selectively to certain endpoints, e.g
[ApiController]
public class PeopleController : ControllerBase
{
[LogDemo]
[HttpGet("demo/endpoint")]
public IActionResult GetAll()
{
}
}
This has the advantage that you might want to pass in some parameters to the attribute, that let you perform certain context specific behaviours.
If you want to add logging to non-controller code, then you can take this approach further to an Aspect Orientated / Decorator pattern.
https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/february/aspect-oriented-programming-aspect-oriented-programming-with-the-realproxy-class
I generally find logging parameters at the controller level is good enough, and something like Application Insights that generates telemetry that shows you what the request looks like is actually the useful bit of information.
When you're saying "method" I'm unsure if you mean literally every method or just whenever a controller is being hit. But middleware would probably be the way to go. The documentation on adding custom middleware is here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1
And here is an example of error handling middleware, where you could place your logging: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.1
I'm trying to make dynamic menu (stored in DB), that is showing on all web app pages.
Using Google I found that it is better to make menu view as a part of Master View (_Layout.cshtml). And because of that, every action method of the controller must contain data with the menu model. To avoid code duplication I found the solution to create a base controller and provide data using its constructor:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/views/passing-data-to-view-master-pages-cs
Also, I'm trying to use async/await possibilities and my PageService (menu) is using ToListAsync() to get data from DB. So now I have a problem, that BaseController constructor has an async method:
public class BaseController : AsyncController, IBaseController
{
private readonly IPageService _pageService;
public BaseController(IPageService pageService)
{
_pageService = pageService;
SetBaseViewModelAsync();
}
private async Task SetBaseViewModelAsync()
{
ViewData["Pages"] = await _pageService.GetAllAsync();
}
}
I know that this is BAD CODE, but I don't know how to design this situation properly. Maybe there is another better way to create the dynamic menu or another way to get data asynchronously?
Also, I found this article, but I don't know if I can apply its solutions because I don't know if I can handle controller instance creation:
http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
Instead of deriving everything from a base controller (which can be a lot of extra work and testing) you can just create a controller called MenuController, create a method called Default and then call it from your Layout:
[ChildActionOnly]
public Default()
{
var viewModel = _pageService.GetAllAsync();
return Partial(viewModel);
}
in your layout:
#{Html.RenderAction("Default", "Menu");}
This is really the easiest and cleanest solution. The biggest PRO is that you can control the cache for the menu separate from the method calling. There are no good solution for asp.net-mvc (1-5) to run Async code in this fashion. (ActionFilters can't be async and (Render)Partials can't be async. You can still call an async method, it will just run Sync.
Render vs Non-Render Performance.
I changed functionality to call Html.RenderAction in my View, as Erik Philips suggested:
#{
Html.RenderAction("Index", "Pages");
}
and controller:
public class PagesController : AsyncController, IPagesController
{
private readonly IPagesService _pagesService;
public PagesController(IPagesService pagesService)
{
_pagesService = pagesService;
}
[HttpGet]
[Route("")]
public async Task<ActionResult> IndexAsync()
{
var viewModel = await _pagesService.GetAllAsync();
return PartialView("MenuPartial", viewModel);
}
}
But RenderAction doesn't work with async controller Actions:
Async PartialView causes "HttpServerUtility.Execute blocked..." exception
So seems like sync call is the only one possible here.
Im currently developing an MVC application in ASP.NET and I'm trying to separate concerns so that I end up with cleaner and more maintanable code.
So, as a starting point, I was thinking of a logging aspect.
My idea is to log (initially) the calling and returning of every method in every controller.
I would have this logic on a separate class, dedicated to logging, so I don't mess my code with logging statements everywhere.
I would also need to have access to the Http Request so I can obtain client information.
Is there an integrated way to do this? Can ASP.NET MVC be used with aspect files just like AspectJ in Java?
Also, can it later be configured to log methods that satisfies certain conditions? (like signature, returning value, exception throwing, etc.)
Thanks very much in advance!
You can use attributes to implement a feature in an aspect-oriented way. Action methods that you want to surround with your functionality then only need to be decorated with your attribute:
[CustomLogger]
public ActionResult Index()
{
// Doing something here ...
return View();
}
You can either decorate a single action method with an attribute, an entire controller, or even apply the attribute globally through ASP.NET MVC's GlobalFilterCollection.
Here's how you'd declare your attribute:
public class CustomLoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
// Here goes your logic
}
// ...
}
The ActionFilterAttribute class allows you to override a couple of methods so you can hook into ASP.NET MVC's action execution pipeline:
OnActionExecuting
OnActionExecuted
OnResultExecuting
OnResultExecuted
You can access request variables through the parameters (like ActionExecutedContext) that are passed to the above methods.