Asp.Net Mvc redirection problem? - c#

My story is: I have a couple of forms in separately views that post description info to edit. And every description belongs to different places. So I'm sending also refTypeId info in my forms to decide which description belongs where. So far ok. but I want to return back to edit description view where form was sent from after editing description. So I need to know where form came from that I just edited in my EditDescription action. My EditDescription action method following code:
[Authorize]
[HttpPost]
public ActionResult EditDescription(string descriptionToEdit, int refTypeId)
{
var entity = _entities.Descriptions.Where(e => e.Id == refTypeId).Select(e => e).FirstOrDefault();
entity.Description = descriptionToEdit;
_entities.SaveChanges();
var Description = _entities.Descriptions.Where(e => e.Id == refTypeId).Select(e => e).FirstOrDefault();
ViewData["Description"] = Description.Description;
TempData["Message"]="Description has been saved";
return (?);// i need to know which view that form was sent from
}

send another parameter called ReturnUrl and redirect to it on success.

Related

Action called from both the same and other controller via RedirectToAction()

I'm integrating an online payment platform inside a currently existing eCommerce platform. This is a B2B platform and customers may pay beforehand (using the aforementionned online payment plaform) or place their orders and are billed at the end of the month.
The following action is located in OrdersController and contains the logic that is triggered once the customer want to confirm its order:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Resume(ResumeViewModel viewModel)
{
if (!_orderService.CanConfirmOrder(UserId, viewModel.TemporaryOrderId))
{
return Resume(viewModel.TemporaryOrderId, _settingsRepository.OrderTooLateMessage());
}
if (_customerService.MustPayDirectly(PaniproId))
{
return RedirectToAction("Initialize", "Payments", new { orderId = viewModel.TemporaryOrderId, amount = viewModel.OrderViewModel.Total });
}
return RedirectToAction("Confirm", new { id = viewModel.TemporaryOrderId });
}
The PaymentsController is responsible for redirecting to the Confirm action if the payment has been successful on the remote payment platform:
public ActionResult Finalize(int orderId)
{
var finalizeResult = _saferpay.FinalizeTransaction((string)_tokens[orderId.ToString()]);
if (finalizeResult.IsSuccess)
{
return RedirectToAction("Confirm", "Orders", new { id = orderId });
}
else
{
LogTools.AddLog(finalizeResult.Error.ToString());
return RedirectToAction("Resume", "Orders", new { id = orderId, errorMessage = finalizeResult.Error.Description });
}
}
So ultimately, both "flows" end in the action below which finally confirms the order and redirect to display its confirmation:
public ActionResult Confirm(int id)
{
return HandleResult(_orderService.ConfirmOrder(UserId, id), order => RedirectToAction("Confirmation", new { id = order.ID }));
}
My worry about the action above is twofold:
I am modifying state in a GET action which should be a stateless operation
Invoking manually the URL /Orders/Confirm/{id} would bypass the payment and is also a very bad thing for the business
Ideally I'd like to do something like adding attributes [HttpPost] and [ValidateAntiForgeryToken] to this action but then it becomes impossible to do a redirect to it. How can I solve this problem ? Is my design flawed and needs a refactor ?
I suggest moving the actual task of confirming the order outside the "confirm" action. That action should simply be for displaying some UI in response to a successful confirmation.
Instead put the logic in some other class which you can call from the other flows directly. Once those flows know that confirmation has been recorded in the database they can redirect to the "confirm" action just to display a message to the user.
And that way if someone decides to visit the "confirm" URL manually then it will just display an incorrect message to them, but won't actually do anything harmful.

asp.net core 2 razor pages route with id

There are two page one is Edit page and the other is Main Detail page which is combined data of some entities
In edit page :
after edit done and I posted the data to API as below
public async Task<IActionResult> OnPostAsync(Guid id)
{
ManufacturerAuthorizedPerson.Id = id;
ManufacturerAuthorizedPerson.ManufacturerId = GetManufacturerId(id);
if (!ModelState.IsValid)
{
await OnGetAsync(id);
return Page();
}
HttpResponseMessage = await httpSystemApi.PutAsync("ManufacturerAuthorizedPersons", ManufacturerAuthorizedPerson);
if (HttpResponseMessage.IsSuccessStatusCode)
{
return RedirectToPage("../Detail", ManufacturerAuthorizedPerson.ManufacturerId);
}
else
{
await OnGetAsync(id);
return Page();
}
}
The ID in OnPostMethod(Guid id) is the value of edited entity. I am using the value and get the other one to use in route as below to get detail page.
ManufacturerAuthorizedPerson.ManufacturerId = GetManufacturerId(id);
but on the detail page the value coming from route ID that I sent from Edit pages post method like below
return RedirectToPage("../Detail", ManufacturerAuthorizedPerson.ManufacturerId);
do not show up as route URL.Instead of ID is beeing same as I wa sent to Edit Page.
Little bit confused.
Need help please.
Suddenly I have found simpe solution to this problem. You should type:
return RedirectToPage("../Detail", new {id = ManufacturerAuthorizedPerson.ManufacturerId});

How to access a UserID when a hyperlink is selected

Here is the hyperlinked code I am generating to show a list of merchants:
<td>
#Html.ActionLink(Html.DisplayFor(modelItem => item.MerchantName).ToHtmlString(), "ViewMerchant", "Merchants")
</td>
What I would like for it to do is to take me to a new page where it shows just the information on the merchant selected (address, webaddress, etc), so a ViewMerchant page in my Merchants View folder.
Can I grab the MerchantID in this ActionLink? If so how would that code look in the above ActionLink?
Secondly in the View Merchants page if anyone could link me to a site that would explain how to build that so the page gets populated with the merchant info would be ideal.
You can pass the MerchantID as a route value as follows:
#Html.ActionLink("Link text", "ViewMerchant", "Merchants", new { id = item.MerchantID }, null )
Where ViewMerchant is the name of your action instide MerchantsController
And here is a small sample to your details action:
public ActionResult Details(int id)
{
Merchant merchant = db.Merchants.Find(id);
return View(merchant);
}
Of course, you should use ViewModels instead of passing your model to the view. But that's another matter on which you can find many information online. Here is one link to start reading about it.

MVCDonutCaching - how to remove Child action cache MVC Donut Caching

I'm using this MVCDonutCaching Nuget package because in the tutorial they said this was possible with child actions.
This question didn't work for me or I didn't understand it correctly.
If someone knows how to delete the child cache with the standard OutputCache attribute, then that is alright as well!
I've searched for this but I can't find it. See here an example:
Index action of the HomeController (homepage):
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
ChildAction of the NewsController:
[AllowAnonymous]
[ChildActionOnly]
[DonutOutputCache(Duration = 600, Location = OutputCacheLocation.Server)]
public PartialViewResult LastArticles(int numberOfArticles)
{
return PartialView("_LastArticles", db.NewsArticles
.Include(i => i.Tags)
.Include(i => i.SeoTags)
.Where(n => n.Status == PublishStatus.Published)
.OrderByDescending(n => n.PublishDate)
.Take(numberOfArticles)
.ToList());
}
Index view of the HomeController:
#{ Html.RenderAction("LastArticles", "News", new { numberOfArticles = 2 }); }
To clear the cache I have an Admin area in my application with a controller and action in it to update the data stored by the child action. So when a news article is updated. The cache should get refreshed on the homepage.
In that action I have the following code:
var cacheManager = new OutputCacheManager();
cacheManager.RemoveItem("News", "LastArticles", new { area = "", numberOfArticles = 2 });
cacheManager.RemoveItem("News", "LastArticles", new { area = "" });
I tried multiple versions but without luck. Can anybody help me?
I believe you shouldn't explicitly define the (empty) area. Although the area's an important part of the routing, MVCDonutCaching does something weird in defining an internal key. Perhaps MVCDonutCache has a bug concerning area's.

Multiple params in ActionLink not rendering as needed in MVC project

I have a view that has multiple partial views on it. It is a settings page. They can click a site on the left panel, ad it will display all the settings for managing that site in the partial view on the page. When I build the links of the sites, I use the following Razor code as an example:
List<Site> siteList = Site.GetSites(new SiteQuery());
foreach (Site site in siteList)
{
<li>#Html.ActionLink(#site.Name, "SelectSite", "SettingsController", new { id = Model.EnvironmentID, siteId = #site.SiteID}, null)</li>
}
And here is the ActionResult I am trying to hit in the SetitngsController controller:
public ActionResult SelectSite(int id, int siteId)
{
//..code here
}
The error occurs thhough, because my URL it outputs is as follows:
~/SettingsController/SelectSite/1?siteId=1
instead of being ~/SettingsController/SelectSite/?id=1&siteId=1344
Am I obvioulsy misunderstanding how to be able to update the current view on the page with a new url and adding params to it this way. Is this wrong in MVC theory, or am I just missing somthing? Thanks in advance!
It is because of your routing table. By default it looks like {Controller}\{Action}\{id}, so your id param is right after action.
You can either change param name or change routing (but the last solution has much more complexity), for example:
#Html.ActionLink(site.Name,
"SelectSite",
"SettingsController",
new { environmentID = Model.EnvironmentID, siteId = site.SiteID}, null)
public ActionResult SelectSite(int environmentID, int siteId)
{
//..code here
}

Categories