Sending HTTP Requests to API from Razor Pages App - c#

I am creating an application that presents users with a form. When the user submits the form, my application takes those values and formats them into a query string. This string is then used to make a call to a third party API. The application is written in C# using an ASP.NET Core Razor Pages template in Visual Studio 2019. I had first experimented with creating an HTTPClient and sending an HTTPRequestMessage to the third party API using hard-coded values in a console app, which worked perfectly. However, when moving my code into a Razor Pages application to add a front end for the app I can't seem to get the application to call the code I created. Since I am only taking form values and passing them in a query string to a third-party API, which doesn't require me to define my own model, I decided to use Razor Pages instead of ASP.NET MVC.
This is the Index.cshtml page as I set it up in my application:
#page
#model IndexModel
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#{
ViewData["Title"] = "Home page";
}
<div>
<form asp-page-handler="ProcessRequest" method="post">
<label for="FirstName">First Name:</label>
<input type="text" name="FirstName" value="" />
<label for="LastName">Last Name:</label>
<input type="text" name="LastName" value="" />
<label for="Email">Email:</label>
<input type="text" name="Email" value="" />
<button type="submit">Send Request</button>
</form>
</div>
As you can see, it is just a simple form with three input fields, nothing fancy.
The underlying Index.cshtml.cs file containing the model logic has the following code:
public class IndexModel : PageModel
{
static HttpClient myAppHTTPClient = new HttpClient();
public async void OnPostProcessRequestAsync()
{
string firstName, lastName, email;
string host = "https://thirdparty.app.com:443/";
string pathname = "path/to/api/endpoint/?operation=create";
firstName = "Test";
LastName = "User";
email = "TestUser#email.com";
string path = pathname + "&first_name=" + firstName + "&last_name=" + lastName + "&email=" + email;
string requestUrl = host + path;
HttpRequestMessage httpRequestMessage = new HttpRequestMessage();
try
{
HttpResponseMessage responseMessage = await myAppHTTPClient.PostAsync(requestUrl, httpRequestMessage.Content);
HttpContent content = responseMessage.Content;
string message = await content.ReadAsStringAsync();
Console.WriteLine("The output from thirdparty is: {0}", message);
RedirectToPage();
}
catch (HttpRequestException exception)
{
Console.WriteLine("An HTTP request exception occurred. {0}", exception.Message);
}
}
}
The documentation for ASP.NET Razor Pages says that when you have an asp-page-handler tag helper in your form submitting the form will call the page handler method OnPost[method_name]Async(). In my case, the line <form asp-page-handler="ProcessRequest" method="post"> in the form should call the public async void OnPostProcessRequestAsync() method. However, this isn't working as I thought it would. I tried using different tag helpers in the form and in the submit button. Is there a way to get the Razor Page to call the method that runs my code. I know I am missing the RedirectToPage() method but I first want to get the method call to work.

Though two people on this thread already suggested the answer, I didn't see a way where I could mark their comments as having answered the question. So, to be fair and give credit where credit is due, their suggestions allowed me to fix my code. Basically, I had defined the page handler method incorrectly. I had defined it as:
public async void OnPostProcessRequestAsync()...
However, this type of application requires page handler methods to return something like Task. When changing the return type from void to Task this worked perfectly. My guess is that since I am defining this code asynchronously, the return value must be something asynchronous. Since the Task<> class is used for Threading (async) code it makes sense that this would work.

Related

How do you pass values from an html file into a c# file to be processed?

I'm trying to pass in values from a html file into a c# file using "get" as my method. my c# code currently looks like this:
namespace animals
{
internal class Program
{
static void Main(string[] args)
{
using (var client = new HttpClient())
{
var endpoint = new Uri("C:/localWebSite/cgi-zoo.html");
var result = client.GetAsync(endpoint).Result;
Console.WriteLine(result);
}
}
}
}
And my form submission line looks like this:
<form name="zooForm" action="C:\localWebSite\theZoo\animals.exe" method="get" onsubmit="return getAnimal();">
What I need to do is pass in 2 variables for use and it needs to be a string and an int, The c# code will then return the user to another page (from the c# code not another html) that displays a picture and some supporting text for the picture. From those data types the most important one is the int because I need to use it to determine what picture I am going to show back to the user and what text document I'm going to use to support that picture.
My problem is I'm having a hard time figuring out how exactly data gets passed from the html into the c# program and when it does eventually get passed how to separate it into the string and the integer.
Yeah, I think you need to be a little more specific. If I had to guess, you want this.
Html Page 1
#*some html*#
<form action="yourAction" method="get">
<input type="number" name="numBer" />
<input type="text" name="someString" />
<input type="submit" value="SUBMIT FORM" />
</form>
then the form will send to the action, here:
public IActionResult yourAction(string fooBar, int numBer)
{
//write in your logic here, like find the picture that matched 'numBer'
//then, when you are ready, do below. I included the parameter because
//you might need it
return RedirectToAction("theNextView", new { numBer = numBer });
}
which will send the user to the next view.
although, maybe i totally misread this question. but thats how I interpret what you are asking. also, you should probably 'post' the data to the action, not 'get'

Convert Blazor HTML markup portion to string for email body

I have a simple Blazor component that take some inputs (some lists of object and few strings) and formats them into simple HTML for display, (tables generated from the lists of objects, simple text, etc.).
This HTML is a report that is intended to be both displayed to users in the app and also emailed to various people (via SendGrid). For compatibility, we are keeping the email HTML as simple as possible.
The component works fine, however I am not sure how to translate a component's markup portion into a simple string of escaped HTML so that I can pass the string to SendGrid and fire off an email.
I am aware of MarkupStrings, but I have only used them in reverse--to write a string containing HTML tags that will be properly displayed in my app. I can't find any suggestions for doing the conversion the way that I need it done.
Is there any simple way to have a component write all of its markup into a string so that I can email it out?
Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?
The simplest way to do that is to employ JSInterop to retrieve the Html markup for the component, produced by the browser. Let's say You've defined a child component, and you want to retrieve its html source. You can do that like this:
Define the child...
SelectGender.razor
<div id="selectGender">
<h1>Select Gender</h1>
<select>
#foreach (var gender in genders)
{
<option>#gender</option>
}
</select>
</div>
#code {
private List<string> genders = new List<string> { "Male", "Female", "Other" };
}
Usage
#page "/"
#inject IJSRuntime JSRuntime
<div>#((MarkupString) html)</div>
<SelectGender />
<button #onclick="GetHtml">Get Html</button>
#code{
private string html;
protected async Task GetHtml()
{
html = await JSRuntime.InvokeAsync<string>("myJsFunctions.getHtml");
}
}
_Host.cshtml
<script>
window.myJsFunctions =
{
getHtml: function () {
return document.getElementById("selectGender").innerHTML;
}
};
</script>
Is there any simple way to have a component write all of its markup into a string so that I can email it out?
No, your C# code has no simple way to do this - you could use JS Interop to get the rendered HTML from the dom, but nothing built in for it.
Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?
That is a possibility - I can't comment on the value of that to you, but it is a technique that could work if the component you are rendering is static,

NavigationManager.NavigateTo in razor pages c# sometimes don't work

i've a very simple question about asp.net core and razor pages.
Inside c# razor code I want to redirect to another route in certain cases.
If there's an async (awaiting) webservice call before, NavigationManager.NavigateTo doesn't work correctly, but no exception is thrown either.
async Task Cancel()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated){
// if there's some async webservice call action here,
// NavigationManager.NavigateTo does neither work nor an exception is thrown
NavigationManager.NavigateTo("/Project", true);
}
}
Do you have any idea what could be the cause?
What are the alternatives to NavigationManager.NavigateTo?
P.S. Please don't propose Response.Redirect in that way (because, that's not suitable for me).
var context = new Microsoft.AspNetCore.Http.HttpContextAccessor();
context.HttpContext.Response.Redirect("/Project", true)
Kind regards,
-he.
Bug is already fixed now.
The problem with NavigationManager.NavigateTo(...) occurred only, because I used a button inside a posting form in my Razor page. Replaced form tag with div and now everything works fine.
Kind regards,
-he
<div>
<p>
<label>Project name: #projectName</label>
</p>
<p>
<!-- [...] -->
<span class="btn-cancel">
<button class="btn-primary" #onclick="#(async () => await Cancel())">Cancel</button>
</span>
</p></div>
#code {
// ...
async Task Cancel()
{
// here're other async action calls
NavigationManager.NavigateTo("/Project", true);
}
}
Another possible solution is using <form #onsubmit="Cancel"> with a <button type="submit">. This preserves advantages of forms, for example the handling of the enter-key.
little late here, but thought to share my case here. For me, NavigationManager.NavigateTo() was not working for some of the target blazor components even though they were simple components with only HTML contents like below, without any C# code in it.
#page "/unauthorized"
<h3>You are not authorized.</h3>
I noticed that "Build Action" property of the components which are not working here aren't set as "Content". So once I made this property as "Content", it started working.

Razor view not sending input to controller?

I am using a razor file for my view with this post form:
#using (Html.BeginForm("Save", "MyEventController", FormMethod.Post))
{
<md-input-container class="md-block" flex-gt-xs="">
<label>Title</label>
<input type="text" name="title" />
</md-input-container>
<md-input-container class="md-block">
<label>Address</label>
<input type="text" name="address" id="txtAddress">
</md-input-container>
<md-button class="md-raised">
<input type="submit" value="Save" />
</md-button>
}
I want to send my input to my controller in Controllers/EventController.cs as show below:
public class MyEventController : Controller
{
[HttpPost]
public void Save(FormCollection collection)
{
InputEvent y = new InputEvent();
y.title = collection["title"];
y.address = collection["address"];
}
}
The Save() methods seems to never be called. how do i submit to Save()?
I am developing in Visual Studio 2015
Remove the text "Controller" when you specify the controller. Change it to,
Html.BeginForm("Save", "MyEvent", FormMethod.Post)
Remove the word "Controller" from your second parameter "MyEventController". So it should be "MyEvent".
Furthermore, I would create a Model (M from MVC) and when the form is posted, instead of receiving FormCollection, it will receive a nice model instead. ASP MVC will take care of binding for you.
Also, as others have pointed out, you may want to eventually return something back to the client to specify if the form submission and processing was successful. Therefore, your action should not return void. Most of the time in cases such as this, you should use the PRG pattern-it is very important to use this pattern.
Try ActionResult instead of void.
From MSDN:
Writes an opening tag to the response. When the user submits the form, the request will be processed by an action method.

Razor Virtual Path issue

The controller is:
public class HomeController : Controller
{
public ActionResult LogOn()
{
return View();
}
[HttpPost]
public ActionResult LogOn(string captcha)
{
if (captcha == HttpContext.Session["captcha"].ToString())
return Content("ok");
else
return Content("failed");
}
public CaptchaImageResult ShowCaptchaImage()
{
return new CaptchaImageResult();
}
}
The view is:
<%using (Html.BeginForm())
{ %>
<p><img src="/Home/ShowCaptchaImage" /></p>
<p>Please enter the string as shown above:</p>
<p><%= Html.TextBox("captcha")%></p>
<p><input type="submit" value="Submit" /></p>
<% } %>
Everything goes well, the captcha image is rendered (CaptchaImageResult writes a jpg in the response stream). But if i use the razor view engine there is no captcha image rendered.
How to fix it?
And one more thing: is it a good way to display captcha?
Later edit: <img src=#Href("../../Home/ShowCaptchaImage") /> works fine.
The problem wasn't because razor it was because in the first example i was using Visual Studio Development server and in the second one i was using IIS
<img src="/Home/ShowCaptchaImage" />
This causes the browser to request /Home/ShowCaptchaImage from the root of the HTTP origin (scheme+domain+port) on which you host the application. You need to map the URL to reflect things like any virtual folders in which the app is hosted. If I'm not wrong and this is the problem you are facing, take a look at ResolveUrl/ResolveClientUrl equivalents for Asp.Net Razor? (I haven't tested it, but it looks good).
Also, you could diagnose the problem easily using Firebug or an equivalent tool in another browser - take a look at the requests which are made and you will see what the browser actually requests.

Categories