I have a public actionresult which I would like to use as a sort of pre-processor for work before handing off to a private action result. I'm planning on using the first controller action to make a call to an external API which places the requesting IP address on a scale measuring potential fraudulent activity of that address. The possible levels that may be returned by this API call are Low, Medium, High.
The general idea of what I'm thinking is as follows:
public async Task<ActionResult> RiskCheck(string id, int page) {
// Check risk for request with external API
var riskLevel = await SomeRiskCheckAsync();
return PageOutput(id, page, riskLevel);
}
[OutputCache(Location = OutputCacheLocation.Server, Duration = Int32.MaxValue, VaryByParam = "id;page;riskLevel")]
private async Task<ActionResult> PageOutput(string id, int page, string riskLevel) {
if (riskLevel.Equals("Low") {
return View("Low_Risk");
} else if (riskLevel.Equals("Medium")) {
return View("Medium_Risk");
} else {
return View("High_Risk");
}
}
The end goal is that there will be 3 cached views corresponding to each unique combination of id and page such that I can alleviate the need to continuously re-render views which will always be the same output.
I have two questions about this approach. First, when applied to a private method like this will the output actually be cached or does it need to be a traditional client facing action result?
Second, I feel like I might be able to combine both views into one if I were able to use a VaryByCustom guard on RiskCheck and use that custom override to check the risk ahead of time. This is actually the approach I went for first. However when I went to override GetVaryByCustomString(HttpContext context, string custom) I realized that there is no async version provided.
public override GetVaryByCustomString(HttpContext context, string custom) {
// Can't await this result
var riskLevel = await SomeRiskCheckAsync();
return riskLevel;
}
I can't afford to not be able to keep this part of the application asynchronous as it will be hit frequently.
To answer your first question:
No the output caching will not be applied. The attribute has to be on the actual public action method because that is all the MVC framework knows about and has the ability to interrogate, whereas MVC knows nothing about your call to PageOutput and even if it did it's arguable what it should do with that piece of information i.e. what if there was another call to another private function with different output cache settings... things could get out of hand rather quickly.
My opinion on the second question:
I think you might be using the async framework for the hell of it, not to solve a problem :) You probably should think about getting rid of it to make life easier. As for combining the views into one, that is something that I'd need more clarification about what the views contain and how likely they are to differ from one another over time as functionality is increased in the product spec.
Related
I'm currently trying to support API versioning using .NET Core API.
I have read many articles about the matter , Couldn't find a really good code examples for it.
Everyone is posting about the controller and how to add API version to each end point but none is actually talking about the headache afterwards. Meaning duplicating the models and the functions (service/handler)
Let's say I have a User controller which has more than 5 end points.
One of these end point is GET User. We needed to remove a field(age field) in the response and it's a breaking change. So we added 2 end point one support the default V1 and the other support V2
[ApiController]
[Route("api/User")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class UserController : ControllerBase {
[HttpGet("user")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> GetUser([FromQuery] string id)
{
return Ok(await _service.GetUser(id));
}
[HttpGet("user")]
[MapToApiVersion("2.0")]
public async Task<IActionResult> GetUserV2([FromQuery] string id)
{
return Ok(await _service.GetUser(id));
}
}
assuming the GetUser() function has a heavy logic ( +30 lines of codes)
the only difference between V1 and V2 is in the model itself one will return the age
and one will not.
What is the better approach to handle such situation?
is it better to duplicate GetUser() as
GetUser(int id)
GetUserV2(int id)
Or pass a version number to the function and do the change accordingly
GetUser(int id , int version)
for my personal opinion. I prefer the duplication as it will be less complicated and easy to read. But duplicating all code also seems useless.
As this is my first time trying to support versioning. I would really appreciate some thoughts and ideas from you !
There is no "one size fits all" solution. What makes sense for your particular application will vary. Here are few ideas that may work for you. There is no preference in order nor is any one particular solution necessarily better than the other. Some options can even be combined together.
Option 1
Move as much logic as possible out of your controllers. Controllers are just a way to represent your API over HTTP. By delegating as much of the logic as possible into collaborators, you can likely reduce a lot of duplication.
Ideally, an action method should be less than 10 lines of code. Extension methods, custom results, and so on can help reduce duplication.
Option 2
Define a clear versioning policy; for example N-2. This can really help clamp down on duplication, but not necessarily eliminate it. Managing duplication across 3 versions is much more manageable if it's unbound.
It should be noted that sharing across versions also comes with some inherent risks (which you might be willing to accept). For example, a change or fix could affect multiple versions and in unexpected or undesirable ways. This is more likely to occur when interleaving multiple versions on a single controller. Some services choose a Copy & Paste approach for new versions to retain the same base implementation, but then allow the implementations to evolve independently. That doesn't mean you can't have shared components, just be careful what you share.
Option 3
Use nullable attributes and ensure your serialization options do not emit null attributes. This obviously doesn't work if you allow or use explicit null values.
For example, the age attribute can be removed using a single model like this:
public class User
{
// other attributes omitted for brevity
public int? Age { get; set; }
}
[HttpGet("user")]
[MapToApiVersion("2.0")]
public async Task<IActionResult> GetUserV2([FromQuery] string id)
{
var user = await _service.GetUser(id);
// if nulls are not emitted, then this effective 'removes' the
// 'age' member using a single model
user.Age = null;
return Ok(user);
}
Option 4
Use an adapter. This could get tedious if you don't have a fixed versioning policy, but is manageable for a limited number of versions. You could also using templating or source generators to render the code for you.
public class User2Adapter
{
private readonly User inner;
public User2Adapter(User user) => inner = user;
public FirstName => inner.FirstName;
public LastName => inner.LastName;
}
[HttpGet("user")]
[MapToApiVersion("2.0")]
public async Task<IActionResult> GetUserV2([FromQuery] string id)
{
return Ok(new User2Adapter(await _service.GetUser(id)));
}
This approach is used for serializing ProblemDetails using Newtonsoft.Json (see here)
This can also be achieved with anonymous types:
[HttpGet("user")]
[MapToApiVersion("2.0")]
public async Task<IActionResult> GetUserV2([FromQuery] string id)
{
var user = await _service.GetUser(id);
var userV2 = new
{
firstName = user.FirstName,
lastName = user.LastName,
};
return Ok(userV2);
}
Option 5
Use a custom OutputFormatter. The default implementation in SystemJsonTextOutputFormatter doesn't honor the specified object type unless the supplied object itself is null. You can change this behavior.
A complete implementation would be a bit verbose, but you can imagine that you might have something like this (abridged):
public class VersionedJsonOutputFormatter : TextOutputFormatter
{
private readonly Dictionary<ApiVersion, Dictionary<Type, Type>> map = new()
{
[new ApiVersion(1.0)] = new()
{
[typeof(User)] = typeof(User),
},
[new ApiVersion(2.0)] = new()
{
[typeof(User)] = typeof(User2),
},
}
public VersionedJsonOutputFormatter(
JsonSerializerOptions jsonSerializerOptions)
{
// TODO: copy SystemJsonTextOutputFormatter implementation
}
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context,
Encoding selectedEncoding)
{
// IMPORTANT: abridged with many assumptions; look at
// SystemJsonTextOutputFormatter implementation
var httpContext = context.HttpContext;
var apiVersion = httpContext.GetRequestedApiVersion();
var objectType = map[apiVersion][context.Object.GetType()];
var ct = httpContext.RequestAborted;
try
{
await JsonSerializer.SerializeAsync(
responseStream,
context.Object,
objectType,
SerializerOptions,
ct);
await responseStream.FlushAsync(ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
}
}
}
This is just one approach. There are plenty of variations on how you can change the mapping.
Option 6
This one area where OData (or even EF) really shines. The use of an Entity Data Model (EDM) separates the model over the wire vs the code model. You can have a single, unified code model with a different EDM per API version that controls how that is serialized over the wire. I'm not sure you can yank only the specific bits that you want for EDM and serialization, but if you can, it just might get you what you want with minimal effort. This is approach is certainly useful for APIs outside of the context of OData.
The OData examples for API Versioning show this at work. I've never tried using things in a purely non-OData way, but that doesn't mean it can't be made to work.
I would prefer the
GetUser(int id , int version)
and add a few comments on why you're using this version varible and use a switch case inside rather than writing duplicate code.
For me personally, writing such duplicate code is not a very good practice as I find it redundant.
So, this is the scenario.
There is a static class which does some calculations based on the data provided.
public static class Calculations
{
public static object Calculate(IList<Class> listOfClass)
{
/// going to database and do calculations....
return calculatedValue;
}
}
Now on page load we are making like 20 AJAX requests simultaneously (only using JQUERY) which is calling different actions in a controller. So for example.
public class ClientController
{
[HttpPost]
public IActionResult GetAGraph(Object model)
{
var listOfClass = // get data based on the model
var calculatedValue = Calculations.Calculate(listOfClass);
var returnObject = //do something with calculatedValue
return Json(returnObject);
}
[HttpPost]
public IActionResult GetSecondGraph(Object model)
{
var listOfClass = // get data based on the model
var calculatedValue = Calculations.Calculate(listOfClass);
var returnObject = //do something with calculatedValue
return Json(returnObject);
}
[HttpPost]
public IActionResult GetThirdGraph(Object model)
{
var listOfClass = // get data based on the model
var calculatedValue = Calculations.Calculate(listOfClass);
var returnObject = //do something with calculatedValue
return Json(returnObject);
}
}
Its not doing the same thing and the actual code I am not allowed to post because of client terms.
What I want to acheive is not doing the same calculations on every controller action, instead if we can store somewhere globally the calculated value and use that for subsequent calls than it would increase the performance and save a lot of time OR any other solution to not make the same calculation again and again.
NOTE: Cannot use memory cache as new data is uploaded every night and it will affect the calculations.
Thank you all for your help!!
There are several ways to optimize the performance, though recommendations would be a bit limited given scarcity of specifics in your question:
Data uploaded every night should not be a showstopper for using memory cache. Your requests are coming nearly simultaneously, so your cache could be short-lived (say a minute). When new data is uploaded, you're only behind by about a minute which in many cases could be acceptable. You can also mark your cache with version of data (i.e. which day) it was constructed with, and thus the moment new data is loaded, ignore and/or purge the cache. So memory caching is still an option.
Precalculating this common interim result (as part of data load) and storing it in the DB, so that on every controller action you start from this common base and just do what is specific for that exact action.
Have your client do a pre-request that would serve as basis for the other requests. On the pre-request, you calculate the common base, issue an id, then have other actions receive this id, load the calculated base off the id, and do their action-specific calculations for final result. The con here, obviously, is enforcing client into a particular usage of the API, but may be acceptable in your case?
If usage pattern is fairly stable - meaning that the set of APIs are frequently called together, simply have a fat API entry point that calculates the base and all specifics of every action in the needed set, and then returns as one massive result, but thus does the common computation only once.
More specifics would help for a more practical answer.
from what i understood you simply need to store the calculatedValue somwhere !
if you need it only in the same run you can stor it in a static global variable,
you can also store it in a session if the value is different between users,
and if the value is the same for all users you can use a file that will be overwritten every time you need to refresh the calculatedValue;
so if the calculatedValue must change once a day you'll have to refresh it once a day
I want to call MarriageById as GET, like this:
var url = '/MarriageById?id=' + id;
But I also want to have a single ActionResult Marriage(Marriage marriage) that does some processing before showing the view. This second one must be POST because it will also receive the "send form" from asp.
I am trying this solution (see my own implementation below), but it is still redirected as a GET and ActionResult Marriage is not found:
[HttpGet]
public ActionResult MarriageById(int id)
{
var marriage = _marriageRepository.GetById(id);
return RedirectToAction(nameof(Marriage), marriage);
}
[HttpPost]
public ActionResult Marriage(Marriage marriage)
{
var people = _personRepository.GetAll();
ViewBag.men = Utils.GetPersonsSelectListByGender(people, isMale: true);
ViewBag.women = Utils.GetPersonsSelectListByGender(people, isMale: false);
return View(marriage);
}
Using RedirectToAction always implies a GET, so this won't work to reach the Marriage action method that only accepts POST.
However there is nothing wrong with calling the other method yourself, it is still a method like any other. So try this instead:
return Marriage(marriage);
And on a side note: if the Marriage method will always only be used to display data, and never to save, store or change data, then using POST is not the best choice. POST typically implies a call with side effects (save, store, change, or even delete), and in general it is best to stick to that convention.
I don't think you should mix GET with POST verbs. They are just semantically different.
If you have a similar functionality that you want to execute for those 2 methods, maybe instead of calling POST from GET you might want to extract the common parts ot some other 'private' method or even layer (depending on the use case).
Hope it makes sense
I am trying to overload an MVC Action, but since "overloading" does not work for route actions (error 500 says ambiguous method I guess because parameters cannot be strongly typed from the browser), then I thought I would just return one action to another since I cannot use RedirectToAction for HttpPost either. The issue is that it is trying to find a view with the new action name instead of what aciton I am trying to call. Here is what I am trying to do:
[HttpPost]
public ActionResult DetailForProductID(int productID)
{
return Detail(new[] { GetProductById(productID) });
}
[HttpPost]
public ActionResult Detail(IEnumerable<Product> products)
{
....
return View(productViewModel);
}
This is the error I get though:
The view 'DetailForProductID' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Products/DetailForProductID.aspx
~/Views/Products/DetailForProductID.ascx
~/Views/Shared/DetailForProductID.aspx
~/Views/Shared/DetailForProductID.ascx
~/Views/Products/DetailForProductID.cshtml
~/Views/Products/DetailForProductID.vbhtml
~/Views/Shared/DetailForProductID.cshtml
~/Views/Shared/DetailForProductID.vbhtml
What is the most robust and elegant way to handle this? I would not want to store things in temporary sessions or do a RedirectToAction becase I should be able to do everything server-side. Any help or advice would be greatly appreciated.
For redirecting like that, I would recommend the following:
return RedirectToAction("Detail", data);
But I am not sure why you have the need for this. Looking at your actions, why not do it like this instead?
public ActionResult Detail(int productId)
{
var data = GetProductById(productID);
....
return View(productViewModel);
}
You can also use Tempdata in this scenario, for example:
public ActionResult DetailForProductID(int productID)
{
IEnumerable<Product> data = GetProductById(productID);
TempData["ProductData"]= data;
return RedirectToAction("Detail",data);
}
public ActionResult Detail(IEnumerable<Product> products)
{
....
if(TempData["ProductData"]!=null){
IEnumerable<Product> data = (IEnumerable<Product>)TempData["ProductData"];
return View(data);
}else {
return View(products);
}
}
I believe it is important to keep controllers "thin" and "dumb". Once you get beyond a simple website and need to build something more complex, you don't want to have to rewrite the same code multiple times (not good for many reasons). It will also become impossible to use the controllers as generic functions (which is basically what you are attempting to do now).
A more elegant and robust way to handle this would be to abstract away your application logic and perform it somewhere else. Then you could call pieces of the logic depending on the action's requirements. To start moving in that direction, you could write the controller specific logic in each controller, then determine what function is shared between both of them and place that somewhere else in your project. In more complex projects it is not unusual to have no application logic in the controller at all.
You might want to try to create one generic function that returns a "Product" probably by Id and place that function somewhere else. Then use the controller to determine the specific logic and call the shared function to get the product by Id.
I have a very large (77 actions) controller that I am using to make a site with wizard-like functionality. The site is like a "job application manager" with multiple components such as an admin component and an end-user component. The component I'm working with is the part where the user would actually fill out a job application. The way things are structured with the other components, it makes the most sense to put all of the job application stuff in the same controller. All of these actions perform similar things but on different models, like so:
public class ExampleController : Controller
{
public ActionResult Action1()
{
Guid appId = new Guid(Session["AppId"].ToString());
... // logic to pull up correct model
return View(model)
}
[HttpPost]
public ActionResult Action1(FormCollection formValues)
{
Guid appId = new Guid(Session["AppId"].ToString());
... // logic to update the model
return RedirectToAction("Action2");
}
public ActionResult Action2()
{
Guid appId = new Guid(Session["AppId"].ToString());
... // logic to pull up the correct model
return View(model)
}
... // on and on and on for 74 more actions
}
Is there any way to reduce some of the constant redundancy that's in every one of the actions? Here is what I am thinking:
Creating a member variable Guid to store the appId and then overriding OnActionExecuting to populate this variable. Is this a good idea?
Implementing some kind of paging to cut down on the number of actions. Any suggestions on how to do that?
I would say yes to your first point and "it depends" to your second. Don't change your design just because you have a lot of methods, if all 77 ActionResult methods make sense to have, then keep them around.
Using a member variable and overriding OnActionExecuting seems like a great way to refactor that appID Guid code into a single place, so you can quickly and easily modify it in the future.
Normally for wizard view, a single action and page is used with multiple divs which can be shown according to the steps.
For example, a registration wizard screen having 4 steps, can be be handled in a single page with divs for each steps. You can make use of JavaScript and css to make it a wizard flow.
Make use of ajax to update different models, if necessary in between the steps.
You may want to put your logic (Job Manager related) in a single repository/manager class. Different controllers associated with different views(e.g AdminController, EndUserController etc) can call methods from same repository/manager class.
Another option could be replacing this..
Guid appId = new Guid(Session["AppId"].ToString());
..with a call to something like the following:
private Guid GetAppId(){
return new Guid(Session["AppId"].ToString());
}
Now you could just use GetAppId() instead wherever you currently use appId. You could of course cache the GUID in the form of a class variable, as you suggest, but it might be a good idea to limit the access and use of that variable to a method like this (get it's value via the method). Might be a little more flexible in case you want to change something later.
As for dividing the page into several pages; sure, go ahead, if it makes sense and feels right. Over 70 actions in one class does sound like a lot. If it makes more sense to keep them there however, you could try to move as much logic as possible out from the methods themselves, and into helper-classes instead. I always try to keep the actions as small as possible, and put the logic in separate classes, each of which is tailored to do one specific thing.
My point is, if each action is no more than 2-4 lines, then 70+ actions is not necessarily a problem.