Programmatically generating request verification token - asp.net-core

Starting with an empty MVC project, here's my Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace AntiForgeryExample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}
And here's my HomeController.cs:
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace AntiForgeryExample
{
public class XyzController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
[HttpGet]
public ContentResult Efg()
{
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = #"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
</form>
</body>
</html>"
};
}
}
}
The following line in Startup.cs adds the anti-forgery middleware:
services.AddMvc();
So, if we go to http://localhost:52838/Xyz/Efg, we get the simple page with a single button:
If we press the button, we get a 400 "Bad Request" response:
I'm assuming this is because we haven't passed a valid request verification token as part of the post. The Fgh method has the ValidateAntiForgeryToken attribute applied:
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
Thus a token is required when calling this method.
As described on this page, normally the code for this token will automatically be included if you use the form tag helper with ASP.NET Core MVC or a Razor Page. It will look something like this and be included as part of the form tag:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
In the example program I show above, we're programmatically generating the HTML with the form.
My question is, is there a way to programmatically generate the required token from C#, without having to go through using an MVC view or Razor Page. The idea would be that we'd get the token value and then include the input tag:
<input name="__RequestVerificationToken" type="hidden" value="TOKEN VALUE HERE">

I shared this question on the /r/dotnet subreddit.
/u/kenos1 provided a very helpful answer there:
You can inject Microsoft.AspNetCore.Antiforgery.IAntiforgery and call GetTokens() on it.
Here’s the documentation: link
As he mentions there, we inject IAntiforgery at the XyzController constructor:
private IAntiforgery antiforgery;
public XyzController(IAntiforgery antiforgery_)
{
antiforgery = antiforgery_;
}
We call GetAndStoreTokens on the IAntiforgery instance that we injected:
var token_set = antiforgery.GetAndStoreTokens(HttpContext);
And finally, we use the resulting token in the generated HTML:
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = string.Format(#"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"">
</form>
</body>
</html>",
token_set.RequestToken)
};
Here is the controller file in its entirety:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System.Net;
namespace AntiForgeryExample
{
public class XyzController : Controller
{
private IAntiforgery antiforgery;
public XyzController(IAntiforgery antiforgery_)
{
antiforgery = antiforgery_;
}
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
[HttpGet]
public ContentResult Efg()
{
var token_set = antiforgery.GetAndStoreTokens(HttpContext);
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = string.Format(#"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"">
</form>
</body>
</html>",
token_set.RequestToken)
};
}
}
}
The official ASP.NET Core 3.1 documentation mentions the GetAndStoreTokens method here.

Related

EditForm on Razor component not firing OnValidSubmit

I have a simple razor component that accepts a comment from a user and then saves the comment. The Page that renders it is currently located at Pages >> Expeditions >> Index.cshtml. When I navigate to /Expeditions in a browser everything loads correctly and the OnValidSubmit works. When I navigate to /Expeditions/Index the page renders properly but the OnValidSubmit is never fired.
I'm guessing there is some type of magic that takes place when I leave out Index in the URL. I'm wondering what I am doing incorrectly here because if I put the component in any page other than an Index page, the Submit button doesn't fire the OnValidSubmit.
Here is the code...
Index.cshtml
#page
#model Project1.com.Pages.Expeditions.IndexModel
#{
ViewData["Title"] = "Home page";
}
#(await Html.RenderComponentAsync<ComposeCommentComponent>(RenderMode.ServerPrerendered, new { PostId = 1 }))
<script src="_framework/blazor.server.js"></script>
ComposeCommentComponent.razor
#using Microsoft.AspNetCore.Components.Web
#using Microsoft.AspNetCore.Components.Forms
#using Project1.com.Models
#using Project1.com.Services
#using System.Security.Claims
#using Microsoft.AspNetCore.Components.Authorization
#inject AuthenticationStateProvider AuthenticationStateProvider
#inject CommentService CommentService
<EditForm Model="#Comment" OnValidSubmit="OnValidSubmit">
<div class="form-group">
<InputTextArea id="Comment" #bind-Value="#Comment.Comment" class="form-control" rows="5" cols="65" placeholder="Leave a Comment!"></InputTextArea>
</div>
<button class="btn btn-primary float-right">Submit</button>
</EditForm>
#functions{
public ClaimsPrincipal User { get; set; }
protected override async void OnInitialized()
{
await base.OnInitializedAsync();
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
User = authState.User;
}
}
#code {
[Parameter]
public int PostId { get; set; }
CommentModel Comment = new CommentModel();
private async void OnValidSubmit()
{
// Update Database with New Comment
CommentService.CreateComment(new CommentModel() { Username = User.Identity.Name, Comment=Comment.Comment, PostId=PostId});
// Clear Comment
Comment.Comment = "";
// Notify Parent Component to Update Data.
await OnNewComment.InvokeAsync(Comment.Id);
}
[Parameter]
public EventCallback<int> OnNewComment { get; set; }
}
Startup.cs
using Project1.com.Data;
using Project1.com.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Project1.com
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddTransient<ExpeditionService>();
services.AddTransient<CommentService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages().RequireAuthorization();
endpoints.MapBlazorHub();
});
}
}
}
If you check the page that doesn't work you'll find there are one or more blazor.server.js 404 errors. Probably this:
Your problem is that you are not specifying a base href for your application.
/Expeditions works because Blazor thinks you're in the root, but
/Expeditions/Index doesn't because it now tries to access resources from the /Expeditions subdirectory.
Blazor uses <base href="~/" /> to define where to get it's resources.
#page
#using StackOverflow.Web.Components
#model StackOverflowAnswers.Pages.MeModel
#{
ViewData["Title"] = "Home page";
}
<!DOCTYPE html>
<html lang="en">
<head>
<base href="~/" />
</head>
<body>
#(await Html.RenderComponentAsync<ComposeCommentComponent>(RenderMode.ServerPrerendered, new { PostId = 1 }))
<script src="_framework/blazor.server.js"></script>
</body>
</html>
Note: #enet's points in his answer still apply to clean up your code.

How to properly refresh a PartialView in Razer page .Net Core 3.1

I have a simple page that also includes a PartialView so that I can refresh the data every 10 seconds returned by the PartialView. It really is a trivial task but I'm not proficient enough with the whole web part and especially JavaScript.
How can I run the LoadData() Method every X seconds so that my partialView shows the data?
I have found the following JS code which I think should be able to refresh the PartialView but I don't know how to adapt this to my situation:
<script type="text/javascript">
$(function () {
setInterval(loadTable, 1000); // invoke load every second
loadTable(); // load on initial page loaded
});
function loadTable() {
$('#data').load('/controller/tabledata');
}
</script>
My index.cshtml looks like this:
#page
#model IndexModel
<div class="data">
<partial name="~/Pages/Shared/_BezoekersData.cshtml" />
</div>
The indexModel:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.SharePoint.Client;
using PnP.Framework;
using System.Security;
namespace MyNameSpace.Pages
{
public class IndexModel : PageModel
{
public IActionResult OnGetPartial() => Partial("\\Shared\\_BezoekersData.cshtml");
// properties left out for clarity
public void OnGet()
{
LoadData();
}
public void LoadData()
{
// dataRetrieval part here.
}
}
}
_BezoekersData.cshtml:
#{
ViewData["Title"] = "Home page";
}
<div style="position:fixed; left:65px; top:25px; color:#c2a149; font-size:75px; ">#Model.Vestiging</div>
<div style="position:fixed; left:1100px; top:180px; color:#c2a149; font-size:50px">#Model.MaxAantal</div>
<div style="position:fixed; left:1100px; top:285px; color:#c2a149; font-size:50px">#Model.HuidigAantal</div>
You should add one more function to call the partial view and refresh it, check this:
public void OnGet()
{
LoadData();
}
public void LoadData()
{
MyModel=new MyModel {thetime=DateTime.Now };
}
public PartialViewResult OnGetMyPartial() // extra method to get data
{
MyModel = new MyModel { thetime = DateTime.Now };
return Partial("_BezoekersData", MyModel);
}
View:
Mind it must be ** div id="data"**, because you use #data to call jquery function.
Pass model data to partial, add model attribute.
And to call the new method, using handler:
In main view: #Model.MyModel.thetime
<div id="data">
<partial name="~/Pages/Shared/_BezoekersData.cshtml" model="Model.MyModel"/> //add a model attribute
</div>
#section Scripts
{
<script type="text/javascript">
$(function () {
setInterval(loadTable, 1000);
loadTable();
});
function loadTable() {
$('#data').load("/Index?handler=MyPartial"); //handler to call razor method
}
</script>
}
This is the partial view:
#model He621.Models.MyModel
In partial:<div style="position:fixed; left:65px; top:100px; color:#c2a149; font-size:75px; ">#Model.thetime</div>
Result:
The time refreshes every 1 seconds, and will only refresh the partial:

Problem with embed RCL in asp.net core with form

I made an RCL(Razor Class Library) for reusable UI.
Here is the code of front-end:
#model Sample.Models.VistorModel
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer Localizer
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form id="ContactForm" asp-action="Contact">
<div>
<h3>#Localizer["Name"]</h3>
<span asp-validation-for="Name"></span>
</div>
<input asp-for="Name" placeholder="#Localizer["NameHint"]" />
<div>
<h3>#Localizer["Phone"]</h3>
<span asp-validation-for="Phone"></span>
</div>
<input asp-for="Phone" placeholder="#Localizer["PhoneHint"]" type="tel" />
<button type="submit">#Localizer["Sumit"]</button>
</form>
Here is the model:
public class VistorModel
{
[Required(ErrorMessageResourceName = "NameError", ErrorMessageResourceType = typeof(Sample.Resources.Views.Contact.Contact))]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "PhoneError", ErrorMessageResourceType = typeof(Sample.Resources.Views.Contact.Contact))]
[RegularExpression(#"((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)", ErrorMessageResourceName = "NotAPhoneNumber", ErrorMessageResourceType = typeof(Sample.Resources.Views.Contact.Contact))]
public string Phone { get; set; }
}
And here is the controller:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sample.Controllers
{
[Route("{culture}/[controller]")]
public class ContactController : Controller
{
[Route("Contact.html")]
public IActionResult Contact()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
[Route("Contact.html")]
public IActionResult Contact(Models.VistorModel Vistor)
{
if (ModelState.IsValid)
{
}
return View();
}
}
}
For example,
the RCL URL is www.sample.com/contact/contact.html
the page URL is www.sample.com/home/index.html(the page is made by asp.net core also)
Now I embedded the RCL into the page.
However, when I click the submit button on the page, the page will redirect to the www.sample.com/contact/contact.html but not www.sample.com/home/index.html.
Besides, I need to embed this RCL not only on the home page but also on any other page.
In a word, what I want to do is:
1.When I input nothing(invalid), the page will redirect to www.sample.com/home/index.html and show the invalid error message on the span which has set attribute asp-validation-for before.
2.When I input name and phone(valid), the page will redirect to www.sample.com/home/index.html and show a message box alert that submits success.
Now I don't know how to make the URL redirect and display the invalid error message correctly. How can I achieve it? Thank you.
As far as I know, if you have set the asp-action, it will generate the form action with /contact/contact not home index.
To solve this issue, you should firstly move the RCL's view to the shared folder and then modify the form tag as below:
<form id="ContactForm" method="post">

How to get RESTful WebAPI ASP.NET Core from Angular 8?

I want to know how to get parameters from data file of image in the controller
in HTTP POST function. This is my upload image
HTML:
<div >
<button mat-button style="color:blue" (click)="onClick()">
<mat-icon>file_upload</mat-icon>
image
</button>
<input
type="file"
#fileUpload
id="fileUpload"
name="fileUpload"
multiple="multiple"
accept="image/*"
style="display:none;"
(change)="handleFileInput($event.target.files)"
[(ngModel)]="model.img"/>
</div>
<img *ngIf="availbule" [src]="imgURL" style="width: 250px;height: 150px;"/>
</div>
<button (click)="uploadFiles()"mat-raised-button
[disabled]="NumberModel.errors||Company.errors||BridgeWidth.errors
||LengthOfRod.errors||LensWidth.errors">add</button>
this is the function that create fromdata and send to the service
ts:
uploadFile(file) {
{
var arr= this.fileToUpLoud.name.split('/');
let ima=arr[arr.length-1];
const fn=new FormData();
fn.append('image',ima);
this.modelService.upload(fn).subscribe();}
this is the service that send to server
service:
public upload(file:FormData):Observable<void>{
return this.httpClient.post<void>("/api/Model/",file);
}
How to get from Angular fromdata hear
controller:
[Route("postItem")]
[HttpPost]
public void addNewModelsImage([FromBody] IFormFile file)
{
var files = HttpContext.Request.Form.Files;
string sub = file.FileName.Substring(file.FileName.IndexOf(".") + 1);
string path = "C:\\Users\\AVITAL\\Desktop\\הנדסאים\\angular\\my-first-project\\src\\assets\\image";
file.CopyTo(System.IO.File.Create(path + file.FileName));
}
To upload image files to your Web API from Angular client app, you can refer to the following example.
On Angular client
fileToUpload: FileList = null;
handleFileInput(files: FileList) {
this.fileToUpload = files;
console.log(this.fileToUpload.length);
}
uploadFile() {
const formData = new FormData();
Array.from(this.fileToUpload).forEach(file => {
// set the key of formdata correctly
// based on the parameter name of your WebAPI action
formData.append("file", file);
});
this.http
.post("https://xxxx/api/model/postItem", formData)
.subscribe(res => {
alert("Uploaded!!");
});
}
Web API Action
public class ModelController : ControllerBase
{
private IWebHostEnvironment _env;
public ModelController(IWebHostEnvironment env)
{
_env = env;
}
[Route("postItem")]
[HttpPost]
public void addNewModelsImage([FromForm] List<IFormFile> file) // use List<IFormFile> for uploaded multiple files
{
//code logice here
Test Result
Note:
1) You can know details about "Static files in ASP.NET Core" from here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-3.1
2) If your client app and back-end service are hosting with different domain, you may need to enable CORS: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1
[Route("postItem")]
[HttpPost]
public void addNewModelsImage([FromForm]IFormFile file)
{
string sub = file.FileName.Substring(file.FileName.IndexOf(".") + 1);
string path = "C:\\Users\\AVITAL\\Desktop\\הנדסאים\\angular\\my-first-
project\\src\\assets\\image\\";
file.CopyTo(System.IO.File.Create(path + file.FileName));
}

Tag helpers not working with DynamicRouteValueTransformer

ASP.NET Core's tag helpers appear not to be working in combination with DynamicRouteValueTransformer.
Here's an MCVE: let's say we have a simple controller with two actions...
public class GenericController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Add()
{
return View();
}
}
... and the Index view links to the Add action with the asp-action tag helper:
<!DOCTYPE html>
<html>
<body>
<a asp-action="Add">Add new!</a>
</body>
</html>
When we now open /Generic in a browser and inspect the page, we'll notice that ASP.NET generated the expected href value for the controller action:
Add new!
So far, so good. Now let's create a DynamicRouteValueTransformer which routes all requests to that same controller...
public class Transformer : DynamicRouteValueTransformer
{
public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
if (values.ContainsKey("controller"))
{
values["originalController"] = values["controller"];
values["controller"] = "Generic";
}
return new ValueTask<RouteValueDictionary>(values);
}
}
... and let's set it up for endpoint routing in Startup.cs...
services.AddSingleton<Transformer>();
...
app.UseEndpoints(endpoints =>
{
endpoints.MapDynamicControllerRoute<Transformer>("{controller=Home}/{action=Index}/{id?}");
});
After opening /Generic (or any other route, e.g. /Foobar) in a browser and inspecting the page, we'll now notice that asp-action tag helper is no longer working:
Add new!
Since the href value is empty, the link is no longer working. It looks the dynamic routing broke the tag helper.
Any suggested fix or workaround?