Controller returns View with modified link - c#

I have a view which is a .cshtml file with a button that executes the function submitForm() when clicked.
function submitForm() {
fetch(`#{REPLACE_URL}#`);
}
In my controller I have an endpoint that modifies the .cshtml and then returns the View, but it shows the default .cshtml, not the modified one.
What I'm trying to to is to modify the #{REPLACE_URL}# value with a proper url. I need to do it in runtime since this url is different between the environments.
public IActionResult ShowViewEndpoint(...)
{
...Modify the "#{REPLACE_URL}#" tag in the View...
return View(builder.HtmlBody);
}
How could I show the modified View? Is there a better way to do it instead the one I'm trying?

Assuming your JS function is in the .cshtml file, you can set the value using Razor:
function submitForm() {
fetch(`#(Model.MyUrl)`);
}
That way you can pass the URL from the server to the frontend

Related

ASP.NET MVC controller method called via getJson not working on server

Obligatory "This works in my dev environment, but doesn't work on the server."
I have an ASP.NET 5 MVC project, using .NET Core, in which actions taken on a certain view trigger a "getJson" call in my javascript code, which in turn calls a function on the controller to obtain data from the server, in order to update the page without a postback.
When a valid entry is made in a textbox, this function is called in my javascript:
function getCustomerOptions(cn) {
$.getJSON('/Home/GetBillingCustomerByCustNum', { cust: cn }, function (data) {
// handle returned json data
}
}
... which calls function GetBillingCustomerByCustNum in my Home controller:
public async Task<JsonResult> GetBillingCustomerByCustNum(string cust)
{
var data = //[retrieve data from server thru repository function]
return Json(data);
}
In my dev environment, this works great. But after I publish the application to an IIS environment on a Windows Server 2016 machine, this fails. It seems that IIS is trying to call '/Home/GetBillingCustomerByCustNum' as though it were a view or a physical object, and so returns a 404 error.
I have tried altering my getJson controller call -- jquery function "getCustomerOptions" -- by adding
<%= Url.Content("~/") %>
so that the call becomes
$.getJSON('<%= Url.Content("~/") %>/Home/GetBillingCustomerByCustNum', { cust: cn }, function (data) { ...
but that still fails. According to the debugger console, the above url is being translated as
http://localhost/HC_RFQ/Home/%3C%=%20Url.Content(%22~/%22)%20%%3E/Home/GetBillingCustomerByCustNum?cust=[given value]
The only other step I could find suggested I prepare my url with an HTML helper, like so:
var url = '#Url.Content("~/Home/GetBillingCustomerByCustNum/")'
$.getJSON(url, { cust: cn }, function (data) {...
but of course that fails because HTML helpers don't work in separate javascript files. Var url is passed in as literally written.
Finally, I know this is not a case of my javascript files not being linked properly, because I can breakpoint my javascript in the debugger ahead of this failing call, and the breakpoints are hit at runtime.
What else can I try to fix this? Any advice is appreciated.
Have you tried a simple Home/GetBillingCustomerByCustNum, just without the starting /? From how you describe the error in production, that's basically a server issue when composing the final route to the controller.
Dropping the Home/ part works because you're calling that action from a view that resides on the same controller's folder path. Since you're using .NET Core, I suggest using the asp-action and asp-controller tag helpers as they let the server decide what's the actual route to the desired methods, even if you're POSTing or GETing without an actual postbacks. For example, this is what I do using javascript to call my methods on a form:
<form asp-controller="myController" asp-action="myAction">
and this is how I get my js code to retrive the corresponding url
let form = $(this).parents('form')[0];
let url = form.getAttribute('action');
the form doesn't have an actual submit button, so that the calls are all made from javascript.

How do I redirect to HTML page from within a controller?

Users receive an email with link that they must click on in order to certify their email address. Once the link is clicked, the user should be redirected to one of two static HTML pages, one saying "You're certified" the other stating "The link is expired"
I have attempted a few options. The first I added a Response.Redirect to my controller with a path to the View. I also tried where I added a routes.MapPageRoute to my RouteConfig file and changed my redirect call to attempt to use this name, but that doesn't work either. I looked at this example for that fix ( Redirect to an html page inside Views Folder )
Here is my code attempting to access the HTML file with the redirect:
EmailCertification.UpdateDBEmailCertified(userName, int.Parse(memberNumber), certSentDT);
return Redirect("~/Views/EmailCertification/EmailCertified.html");`
The error I get is that:
Path to /Views/EmailEmailCertification/EmailCertified.html is not found. I verified the spelling and the path is all is correct.
If I changed my code to include MapPageRoute in RoutesConfig it still doesn't work.
Here is my route config:
routes.MapPageRoute("HtmlPage", "EmailCertifiedURL", "~/Views/EmailCertification/EmailCertied.html");`
Here is my controller:
return Redirect("EmailCertifiedURL");
Here is my controller in action, it is a HttpPost
public ActionResult EmailCertify(string userName, string memberNumber, string certSentDate)
{
DateTime certSentDT;
long lngCertSent = long.Parse(certSentDate);
certSentDT = new DateTime(lngCertSent);
if (certSentDT < DateTime.Now.AddDays(-14))
return Redirect("EmailOldURL");
EmailCertification.UpdateDBEmailCertified(userName, int.Parse(memberNumber), certSentDT);
return Redirect("~/Views/EmailCertification/EmailCertified.html");
}
The error I get on this is that
the controller doesn't have a action EmailCertifiedURL. This code I took from the above mentioned StackFlow article.
All I want is the email link to fire off the controller action EmailCertify and redirect me to a static HTML page.
https://localhost:44344/EmailCertification/EmailCertify?userName=IS&memberNumber=3000050&certSentDate=636959314302036120
That seems strange. A work around could be adding a new action that returns your entire html with no layout. I mean, try with this
public ActionResult CertifiedEmail(){
return View();
}
Then you should create a view for your action with the same name ( CertifiedEmail.cshtml ), and inside your View paste all your html. At the beginning you should add this code to remove the Layout
#{
Layout = null;
}
I tend to use RedirectToAction() methods instead of just Redirect()
The 2nd parameter will need to be the name of the controller if it is a different controller.
return RedirectToAction("EmailCertifiedURL", "EmailCertification");
public ActionResult Questionnaire()
{
return Redirect("~/MedicalHistory.html");
}

Make HttpPost request to an action method in an ASP.NET MVC controller

I am trying to build a functionality where I need to a create a candidate's profile in our application. There are two steps/UI's to create a candidate's profile:
1 - Create template...where the user enters candidate's information.
2 - Preview template...where the user will be shown a preview of how their profile would look like once they add the profile to our system.
I have already created the views to support these UI's via a controller called "CandidateController" which contains few action methods:
1- [HttpGet] "Create" that returns a Create template.
[HttpGet]
public ViewResult Create()
2- [HttpPost] "Preview" that returns a Preview template.
[HttpPost]
public ActionResult Preview(ProfileViewModel viewModel)
Now what I need to implement is to have a button/link in the Create template that would call the action method [HttpPost] Preview in the controller.
Challenge
I am also wondering if there is a way that the model binder would load the ViewModel object for me if am able to call the HttpPost Preview action method from the first create template.
I am looking for a suggestion/help to how to best achieve this kind a functionality.
Any help will be deeply appreciated.
Challenge I am also wondering if there is a way that the model binder
would load the ViewModel object for me if am able to call the HttpPost
Preview action method from the first create template.
You could use either a standard form or an AJAX call to invoke the Preview POST action and pass all the property values of the view model then. All the values you pass in this request will be the values that will be bound by the default model binder. Here's an article explaining how the default model binder expects the parameters to be named for more complex structure such as lists and dictionaries.
Example with AJAX:
$.ajax({
url: '#Url.Action("Preview")',
type: 'POST',
data: { Prop1: 'value 1', Prop2: 'value 2' },
success: function(result) {
// TODO: do something with the result returned from the POST action
}
});
If you don't want to use AJAX you could use a standard form with hidden fields:
#using (Html.BeginForm())
{
#Html.Hidden("Prop1", "value 1")
#Html.Hidden("Prop2", "value 2")
...
<button type="submit">Preview</button>
}
OK so here are the options that I had to get around:
As Darin suggested you may go with the unobtrusive way by using $.ajax(options), however the thing is you might want to go this way only if you want to do a partial page update or if you want to work on updating/dumping new html in the same view.
And if you don't want to use the Ajax, instead of using Hidden fields, you can simply use the TempData property in MVC, this is how I implemented my targeted functionality using TempData. p.s.below...
[HttpPost]
public ActionResult Create(ViewModel viewModel)
{
this.TempData["profile"] = viewModel;
return RedirectToAction("Preview");
}
public ActionResult Preview()
{
if (TempData["profile"] != null)
{
return View((ViewModel)TempData["profile"]);
}
// Handle invalid request...
return null;
}
So, this solution worked pretty well for me, where I did not write any JavaScript or unnecessary HTML. AND thanks Darin for directing me to a starting point.

MVC 3: How to render a view without its layout page when loaded via ajax?

I am learning about Progressive Enhancement and I have a question about AJAXifying views. In my MVC 3 project I have a layout page, a viewstart page, and two plain views.
The viewstart page is in the root of the Views folder and thus applies to all views. It specifies that all views should use _Layout.cshtml for their layout page. The layout page contains two navigation links, one for each view. The links use #Html.ActionLink() to render themselves to the page.
Now I have added jQuery and want to hijack these links and use Ajax to load their content on the page dynamically.
<script type="text/javascript">
$(function () {
$('#theLink').click(function () {
$.ajax({
url: $(this).attr('href'),
type: "GET",
success: function (response) {
$('#mainContent').html(response);
}
});
return false;
});
});
</script>
There are two ways I can think of to do this, but I don't particularly like either one:
1) I can take the entire View's contents and place them in a partial view, then have the main view call the partial view when it is rendered. That way, using Request.IsAjaxRequest() in the controller, I can return View() or return PartialView() based on whether or not the request is an Ajax request. I can't return the regular view to the Ajax request because then it would use the layout page and I'd get a second copy of the layout page injected. However, I don't like this because it forces me to create empty views with just a #{Html.RenderPartial();} in them for the standard GET requests.
public ActionResult Index()
{
if (Request.IsAjaxRequest())
return PartialView("partialView");
else
return View();
}
Then in Index.cshtml do this:
#{Html.RenderPartial("partialView");}
2) I can remove the layout designation from _viewstart and specify it manually when the request is NOT Ajax:
public ActionResult Index()
{
if (Request.IsAjaxRequest())
return View(); // Return view with no master.
else
return View("Index", "_Layout"); // Return view with master.
}
Does anyone have a better suggestion? Is there a way to return a view without its layout page? It would be much easier to explicitly say "don't include your layout" if it is an ajax request, than it would be to explicitly include the layout if it's not an ajax.
In ~/Views/ViewStart.cshtml:
#{
Layout = Request.IsAjaxRequest() ? null : "~/Views/Shared/_Layout.cshtml";
}
and in the controller:
public ActionResult Index()
{
return View();
}
Just put the following code on the top of the page
#{
Layout = "";
}
I prefer, and use, your #1 option. I don't like #2 because to me View() implies you are returning an entire page. It should be a fully fleshed out and valid HTML page once the view engine is done with it. PartialView() was created to return arbitrary chunks of HTML.
I don't think it's a big deal to have a view that just calls a partial. It's still DRY, and allows you to use the logic of the partial in two scenarios.
Many people dislike fragmenting their action's call paths with Request.IsAjaxRequest(), and I can appreciate that. But IMO, if all you are doing is deciding whether to call View() or PartialView() then the branch is not a big deal and is easy to maintain (and test). If you find yourself using IsAjaxRequest() to determine large portions of how your action plays out, then making a separate AJAX action is probably better.
All you need is to create two layouts:
an empty layout
main layout
Then write the code below in _viewStart file:
#{
if (Request.IsAjaxRequest())
{
Layout = "~/Areas/Dashboard/Views/Shared/_emptyLayout.cshtml";
}
else
{
Layout = "~/Areas/Dashboard/Views/Shared/_Layout.cshtml";
}
}
of course, maybe it is not the best solution
You don't have to create an empty view for this.
In the controller:
if (Request.IsAjaxRequest())
return PartialView();
else
return View();
returning a PartialViewResult will override the layout definition when rendering the respons.
With ASP.NET 5 there is no Request variable available anymore. You can access it now with Context.Request
Also there is no IsAjaxRequest() Method anymore, you have to write it by yourself, for example in Extensions\HttpRequestExtensions.cs
using System;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Mvc
{
public static class HttpRequestExtensions
{
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
return (request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest");
}
}
}
I searched for a while now on this and hope that will help some others too ;)
Resource: https://github.com/aspnet/AspNetCore/issues/2729
For a Ruby on Rails application, I was able to prevent a layout from loading by specifying
render layout: false in the controller action that I wanted to respond with ajax html.

Retrieve URL for Action from Controller

I am calling a Controller Action from a view, within that controller I need to invoke another Action which I will invoke to save the view to a network location as either HTML or Image.
How do I retrieve the URL to an Action from within a Controller. Please note I need the actual URL, this means RedirectionToAction or View() wont work.
Why? I need to pass in a URL which will contain a call to a View. This view will be used to generate an image or HTML document using the System.Windows.Forms.WebBrowser.
.NET 3.5; C#; MVC 1;
I could do something like this, but its dirty ... well it leaves me with that dirty feeling.
using(Html.BeginForm("Action", "MyWorkflowController",
new {
MyId = "bla",
URLToGenerateImage = Url.Action("GenerateImage", "MyWorkflowController")
}))
I ended up using the MvcContrib.UI.BlockRenderer to convert to View to Html instead of generating the image. I proceeded to save the html string to a file system location.
Here is a link for further information
http://www.brightmix.com/blog/how-to-renderpartial-to-string-in-asp-net-mvc/
How about ContentResult - Represents a text result, so you could have
/Controller/GetUrl/id
Public ActionResult GetUrl(int id)
{
// builds url to view (Controller/Image/id || Controller/Html/id)
var url = BuildImageUrl(id);
return ContentResult(url);
}
in view you could have:
GenerateImage

Categories