How can I make the following action method thread safe? - c#

The following two links access the same action method:
http://stage.bullydog.com/Products/accessories/podmount and http://stage.sctflash.com/Products/accessories/podmount
I use Request.Url.Host to determine the brand of products I want to return from a database. When I access http://stage.bullydog.com/Products/accessories/podmount first, Request.Url.Host contains the value stage.bullydog.com, but if I then go to http://stage.sctflash.com/Products/accessories/podmount, Request.Url.Host may contain stage.sctflash.com or it may contain stage.bullydog.com.
The action method that is called is:
public ActionResult GetAccessoriesByType(RenderModel model, string id)
{
Common _common = new Common();
string brand = Request.Url != null ? _common.GetProductBrand() : BrandType.SCT;
var productSearchResultsModel = new ProductSearchResultsModel
{
Accessories = _accessoryRepository.GetAccessoriesByType(id, brand)
};
return View("~/Views/accessories.cshtml", productSearchResultsModel);
}
The code that gets the brand is:
public class Common
{
public string GetProductBrand()
{
var host = HttpContext.Current.Request.Url.Host;
if (host.Contains("sctflash"))
return BrandType.SCT;
if (host.Contains("bigrig") || host.Contains("bigrigs"))
return BrandType.BigRig;
if (host.Contains("bullydog"))
return BrandType.BullyDog;
return BrandType.SCT;
}
}
How can I ensure the Request.Url.Host contains the proper host when the same action method is accessed from two different hosts?
You can see this in action if you go to http://stage.bullydog.com/Products/accessories/podmount and then go to http://stage.sctflash.com/Products/accessories/podmount and refresh either one, the logo should change which means that the value returned by Request.Url.Host was incorrect.
Also, can GetProductBrand be static and still be thread-safe in this case?

The reason why accessing another website was bringing back the one websites content and the Request.Url.Host had the other websites information was because I had declared OutPutCache[Duration=60] at the top of my controller. If I waited 60 seconds and then refreshed, the correct data for each website would return. Once I removed this from the controller, everything worked as expected.

Related

How to hide parameters from querystring (url) ASP.NET

I'm trying to hide the parameters from the querystrings in my web application.
I have been able to do that by using the session to store temporary variables. So it would work like this:
1. Click the view profile button:
href="#Url.Action("RedirectWithId", "Redirect", new { act = "ProfileView", ctrl = "User", id = member.Id})"
2. Calls the redirection method and stores the temp data:
public class RedirectController : Controller
{
public ActionResult RedirectWithId(string act, string ctrl, int id)
{
Session["temp_data"] = id;
return RedirectToAction(act, ctrl);
}
}
3. Use it in the action method without the parameter:
public ActionResult ProfileView()
{
if (Session["temp_data"] == null)
{
return Redirect(Request.UrlReferrer.ToString());
}
int id = (int)Session["temp_data"];
var model = GetUserById(id);
return View(model);
}
So it works just fine, however, this way to hide parameters doesn't handle the case where let's say I go to a first profile(id 4), and then go to a second one(id 8). If from the second profile I press the back button on the navigator trying to go back to the first profile(id 4), I'm going to be redirected to the current profile(id 8), since 8 is the current value of the Session["temp_data"].
Is there a way to handle this perticular case? Or is the another totally different and better way to hide parameters in the URL?
Thank you!
You can try this instead of Session
TempData["temp_data"]
I came to the conclusion that since I am already using authorizations and roles within my application, I don't need to always hide the parameters. I can simply hide whenever I am passing a complex object as a parameter.

retrieving session variable from another controller

Is is possible to retrieve a Session variable from a different controller it was created from?
I create this in my Account controller
System.Web.HttpContext.Current.Session["Session"] = sessionGuid;
and from a different controller I'm trying
if (System.Web.HttpContext.Current.Session["Session"] != null)
{
return Json(System.Web.HttpContext.Current.Session["Session"].ToString());
}
return null;
which always returns null.
as a Test I added some code to my Login controller and in that part i do get the value out of it
if (System.Web.HttpContext.Current.Session["Session"] != null)
{
test = System.Web.HttpContext.Current.Session["Session"].ToString();
}
else
{
test = "no";
}
i always got the actual result, not the"no" part of the code
For some reason its different into how i have to access the session, at the other controller i changed to this code and it worked
if (HttpContext.Session["Session"] != null)
{
return Json(HttpContext.Session["Session"].ToString());
}
return null;
if controller is within the same website/virtual directory then YES, however if you are saying your session was created in a different site and you trying to access it within a controller which belongs to different site then you cannot.

No need for separate controllers or actions for each view solution - understanding the code

There are two things I can't grasp in this article. It explains the way to use ASP.NET MVC without the need for separate controllers or actions for each view.
1) In DispatchRequest method:
private void DispatchRequest(IControllerFactory controllerFactory, string controller, string action)
{
var route = GetRoute(controller, action);
_requestContext.RouteData.Values["x-action"] = action;
_requestContext.RouteData.Values["x-controller"] = controller;
if (route != null)
{
_requestContext.RouteData.Values["controller"] = route.Controller;
_requestContext.RouteData.Values["action"] = route.Action;
if (route.Area != string.Empty)
{
_requestContext.RouteData.DataTokens["area"] = route.Area;
}
controller = route.Controller;
the action and controller strings are stored under "x-action" and "x-controller" keys. A few lines below the controller and action are stored under "controller" and "action" keys.
Both pairs (controller and action) are strings, aren't these pairs the same? It looks to me like they are. Why duplicate the data unnecessarily?
2) In the controller, ControllerLessController :
public virtual ActionResult Index()
{
var action = RouteData.Values["x-action"].ToString();
var controller = RouteData.Values["x-controller"].ToString();
RouteData.Values["action"] = action;
RouteData.Values["controller"] = controller;
if (RouteData.Values["area"] != null)
{
RouteData.DataTokens["area"] = RouteData.Values["area"].ToString();
}
return View(action);
}
}
Notice the first two lines in the body. Why invoke toString on string objects? Moreover, why somebody decided to store them in action and controller variables and overwrite the data under "action" and "controller" keys (line 3,4)?
Both pairs (controller and action) are strings, aren't these pairs the same? It looks to me like they are. Why duplicate the data
unnecessarily?
No. The intent is to store the original request values in "x-action", "x-controller" and then overwrite "action", "controller" as necessary while still having access to the original values at a later stage in processing. "x-action", "x-controller" are simply being used as temp vars. They are being stored in RouteData because once the dispatch method completes any local vars will go out of scope.
Notice the first two lines in the body. Why invoke toString on string objects?
RouteData.Values returns an object via a string indexer hence the ToString. i.e.
RouteData.Values["MyValue"] returns an object not a string.
Moreover, why somebody decided to store them in action and controller
variables and overwrite the data under "action" and "controller" keys
(line 3,4)?
This goes back to the TempData idea in 1. An action request comes in. Normally in MVC that would translate to a controller with a view but here in this controller-less example controller-less actions need to be mapped to the controllerless-handler.
So in DispatchRequest these are overridden to point at the controllerless handler class ControllerLessController : Controller.
Note this is happening prior to controller selection.
Then MVC processes the request in the normal fashion but because of the switching around in Dispatch MVC doesn't go looking for the originally requested controller (since there wasn't one) instead it uses the injected controller-less controller:
_requestContext.RouteData.Values["action"] = _configuration.DefaultAction;
controller = _configuration.DefaultController;
So then the normal request processing carries on and lands within the controllerless controller. At that point we need to reach back and find which view was originally requested.
public virtual ActionResult Index()
{
var action = RouteData.Values["x-action"].ToString();
var controller = RouteData.Values["x-controller"].ToString();
RouteData.Values["action"] = action;
RouteData.Values["controller"] = controller;
if (RouteData.Values["area"] != null)
{
RouteData.DataTokens["area"] = RouteData.Values["area"].ToString();
}
return View(action);
}
This info was stored in the "x-action" routes values so they pull that out and return a view for the original request:
return View(action);
where
var action = RouteData.Values["x-action"].ToString();
Basically you just have a layer of re-direction/interception. We re-direct all actions without controllers to some handler, in this case the ControllerLessController. Before we do that we need to store the original request params in "x-action" vars. Then once in the ControllerLessController handler we pull those original values back out so we can produce a view for the original controller request.

ASP.Net MVC Refresh Page without destroying ViewModel

I want to create a multilingual webpage. To switch between languages I've got a dropdown on my page. If the change event of the dropdown gets fired the Method called "ChangeLanguage" in my Controller is called.
public ViewModels.HomeViewModel HVM { get; private set; }
// GET: Home
public ActionResult Index()
{
this.HVM = new ViewModels.HomeViewModel();
return View(this.HVM);
}
public JsonResult ChangeLanguage(int id) {
return Json(new {Success = true});
}
Now I'd like to to change my "SelectedLanguage" Property in my ViewModel (HVM) - but the Reference is null. May anyone explain why HVM is null in my ChangeLanguage Method?
After my SelectedLanguage Property is changed I'd like to reload my whole page to display it's texts in another language
e.g.
#model ViewModels.HomeViewModel
<html>
<div class="HeaderText">
Text = #{
#Model.TextToDisplay.Where(o =>
o.Language.Equals(Model.SelectedLanguage)).First()
}
</div>
Here's what I want to do in PseudoCode:
PseudoCode:
public JsonResult ChangeLanguage(int id) {
this.HVM.SelectedLanguage =
this.HVM.AvailableLanguages.Where(o =>
o.ID.Equals(id)).First();
Page.Reload();
return Json(new {Success = true});
}
May anyone explain why HVM is null in my ChangeLanguage Method?
Adhering to stateless nature of HTTP protocol, all (unless explicitly added into request header) requests (MVC method calls) loose state data associated with it. Web server treats every request a new request and creates new instances of classes right from controller itself.
In your case since it is a new request, controller has a HVM property defined but in ChangeLanguage it is not instantiated (it gets instantiated only into Index method which is not called when you invoke ChangeLanguage) hence it is null.
After my SelectedLanguage Property is changed I'd like to reload my
whole page to display it's texts in another language.
Option 1: Refresh page
Simple option to implement. Pass the language selection to server, server will return a new view with specific data. Drawback, whole page will refresh.
Option 2: Update view selectively
If option 1 is really not acceptable, then consider this option. There are multiple ways you can achieve it. Basically it involves either (a) breaking you view into partial view and update only the portion that is affect by selection or (b) bind data element with a JS object.
(a) - Not much need to be said for this.
(b) - Data binding can easily be done if you employ a JS library like KnockoutJS.
Change your methods to these methods , This trick will work for you =>pass your model to Change language from view. Also update JsonResult to ActionResult.
public ActionResult ChangeLanguage(ViewModels.HomeViewModel model,int id)
{
this.HVM.SelectedLanguage =
this.HVM.AvailableLanguages.Where(o =>
o.ID.Equals(id)).First();
return RedirectToAction("Index",model);
}
public ActionResult Index(ViewModels.HomeViewModel model)
{
if(model == null)
{
this.HVM = new ViewModels.HomeViewModel();
}
return View(this.HVM);
}

How are args represented (if at all) in Web API Attribute Routing annotations and if they aren't, how are they discovered?

I am currently using attribute routings such as this:
[Route("api/InventoryItems/{ID}/{packSize:int}/{CountToFetch:int}")]
...and am using even longer ones (with more "pieces," or arguments), and in practice they work fine.
However, I need to refactor this (args masquerading as part of the path considered bad practice) so that the URI passed by the client is not like so:
//http://<machineName>:<portNum>/api/InventoryItems/<IDVal>/<packSizeVal>/<CountToFetchVal>
http://platypus:8675309/api/InventoryItems/42/24/50
...but rather like this:
//http://<machineName>:<portNum>/api/InventoryItems/?<argName>=<argVal>?<argName>=<argVal>?<argName>=<argVal>
http://platypus:8675309/api/InventoryItems?ID=42?packSize=24?CountToFetch=50
Currently I can grab the args passed within the "path" info and pass them from the Controller (where they arrive) to the Repository (which uses them to get the precise data required).
For example, this Controller method:
[System.Web.Http.Route("api/Departments/{ID:int}/{CountToFetch:int}/{dbContext=03}")]
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch, string dbContext)
{
return _deptsRepository.Get(ID, CountToFetch, dbContext);
}
...has the values passed via a URI from the client assigned to the method parameters. What, if anything, do I need to change in this code for args passed in the URI via the "?=" method to also be assigned to the method parameters?
Can I do this, with those args simply stripped out of the Attribute Routing annotation, like so:
[System.Web.Http.Route("api/Departments")]
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch, string dbContext)
{
return _deptsRepository.Get(ID, CountToFetch, dbContext);
}
?
...or possibly leave it as-is, with just the format of the URI changing (but nothing in the Controller)?
UPDATE
I wasn't expecting it to work, but I can definitely verify that leaving the server code as is, and replacing the URI with this jazz:
"?<argName>=<argVal>"
...does not work - it returns null without even hitting my Controller method!
UPDATE 2
With an URI like this:
http://localhost:28642/api/InventoryItems/PostInventoryItem?id=42?pack_size=12?description=ValuableDesc?vendor_id=venderado?department=42?subdepartment=85?unit_cost=2.50?unit_list=3.75?open_qty25.25?UPC_code=12345?UPC_pack_size=24?vendor_item=someVendorItem?crv_id=9898987?dbContext=03
...I can reach the Controller if I remove all args from the routing attribute and the method signature:
[Route("api/InventoryItems/PostInventoryItem")]
public void PostInventoryItem()
{
HandheldServerGlobals.SaveTypes st = HandheldServerGlobals.SaveTypes.CSV; //Works (C:\Program Files (x86)\IIS Express\SiteQuery3.csv created) // <-- I reach the breakpoint on this line, but...:
//commented out for now:
//_inventoryItemRepository.PostInventoryItem(id, pack_size, description, vendor_id, department, subdepartment, unit_cost, unit_list, open_qty, UPC_code, UPC_pack_size, vendor_item, crv_id, dbContext, st);
}
...but where/how do I get the args passed in the URI now?
By annotating the Controller method with "[FromURI]":
[Route("api/InventoryItems/PostInventoryItem")]
public HttpResponseMessage PostInventoryItem([FromUri] InventoryItem ii)
{
_inventoryItemRepository.PostInventoryItem(ii.ID, ii.pksize, ii.Description, ii.vendor_id, ii.dept,
ii.subdept, ii.UnitCost, ii.UnitList, ii.OpenQty, ii.UPC, ii.upc_pack_size, ii.vendor_item, ii.crv_id);
var response = Request.CreateResponse<InventoryItem>(HttpStatusCode.Created, ii);
string uri = Url.Link("DefaultApi", new { id = ii.ID });
response.Headers.Location = new Uri(uri);
return response;
}
...and by replacing all but the first "?" in the URI passed in with "&"

Categories