XSRF token mismatch - broadleaf-commerce

Hi I am trying to handle a form submission via a custom controller in the admin section
This is my test controller
#Controller
#RequestMapping("/" + TempController.SECTION_KEY)
public class TempController extends AdminAbstractController {
protected static final String SECTION_KEY = "test2";
#RequestMapping(value = "", method = RequestMethod.GET)
public String test(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
// This is expected by the modules/emptyContainer template, this is a custom template that gets included into the body
model.addAttribute("customView", "views/test2");
ShippingEntity shp=new ShippingEntity();
model.addAttribute("shipping",shp);
// ensure navigation gets set up correctly
setModelAttributes(model, SECTION_KEY);
// gets the scaffolding set up to display the template from the customView attribute above
return "modules/emptyContainer";
}
#RequestMapping(value = "", method = RequestMethod.POST)
public String testPost(HttpServletRequest request, HttpServletResponse response, Model model,#ModelAttribute ShippingEntity shp) throws Exception {
// This is expected by the modules/emptyContainer template, this is a custom template that gets included into the body
model.addAttribute("customView", "views/test2");
System.out.println(shp.getLink());
System.out.println(shp.getTrackingNumber());
model.addAttribute("shipping",shp);
// ensure navigation gets set up correctly
setModelAttributes(model, SECTION_KEY);
// gets the scaffolding set up to display the template from the customView attribute above
return "modules/emptyContainer";
}
}
And this is the view template :
<div class="row">
<div class="twelve columns">
<form action="#" th:action="#{/test2}" th:object="${shipping}" method="post">
<p>Id: <input type="text" th:field="*{trackingNumber}" /></p>
<p>Message: <input type="text" th:field="*{link}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</div>
</div>
The problem is that when I submit the values I get the error:
XSRF token mismatch (null). Session may be expired
I understand that this has to do with security issues however I can't find a way to make it work.
Any tips how to solve this?

Apparently it was simpler than I thought, maybe it will help someone in the future.
Just change <form> </form> to <blc:form></blc:form>

Related

If my button is in the form, it does not send any data back to the view

I am trying to get some more information about Razor Pages with core 2.0 and I am having some issues with the post. Please note, this is not production worthy, its just code to try and getting a better understanding of what you can do in razor pages.
The issue is, if my button is in the form, it does not send any data back to the view. So, computedURL in OnPostConcatURL() is never getting to the view.
If the button is outside the form, the form data does not get to the post controller OnPostEdit23 but I can send back ComputedURL to the view. Clearly I am missing something or a lot. And I cannot find an example to fix this.
#page
#model VisibilityTest
#{ ViewData["Title"] = "Visibility Test Site"; }
<form id="frmVisibility" method="post">
<div class="container">
<div class="row form-group">
<div class="col-md-1"> Select Portal: </div>
<div class="col-md-1"><select id="ddlEnvironment" asp-for="selectedEnvironment" asp-items="Model.Environments"></select></div>
<div class="col-md-2"><select id="ddlPortalName" asp-for="selectedPortalName" asp-items="Model.portalNames"></select></div>
<div class="col-md-3"><input asp-for="#Model.ComputedURL" /></div>
</div>
<div class="row form-group">
<div class="col-md-1"><button id="btnConcatURL" asp-page-handler="ConcatURL" type="submit" class="btn btn-primary">Submit23</button></div>
</div>
</div>
</form>
<form method="post">
<button asp-page-handler="edit23" class="btn btn-default">Edit2</button>
</form>
[BindProperty]
public string ComputedURL { get; set; }
public void OnGet()
{
config = GetConfigFile();
PopulatedEnvironmentSelectList();
PopulatePortalNameSelectList(config);
}
public IActionResult OnPost()
{
ComputedURL = selectedEnvironment + selectedPortalName;
return RedirectToPage("./VisibilityTest");
}
public void OnPostConcatURL()
{
ComputedURL = "this is a test";
}
public void OnPostEdit23()
{
ComputedURL = "this is a test";
}
I'm still figuring out Razorpages as well but I noticed a couple of points about your example:
The Model should be the code behind page class not some other object.
The BindProperty should probably be to an object and not just a string (or you can bind to multiple properties on the code-behind object)
If you want to pass back just a message (string) you can use temp data.'
You definitely want the button inside the form as it will populate the model properties with form values (but as with #2 the binding property should be more than a simple string).
So your example modified example below seems to do what you are looking for (I simplified the dropdowns to text fields for easy testing).
.cshtml page
#page
#model VisibilityTestModel
#{
ViewData["Title"] = "Visibility Test Site";
}
<hr />
<form id="frmVisibility" method="post">
<div class="container">
<div class="row form-group">
<div class="col-md-1"> Select Portal: </div>
<div class="col-md-3"><input asp-for="#Model.Portal.Environment" /></div>
<div class="col-md-3"><input asp-for="#Model.Portal.Name" /></div>
<div class="col-md-3">#Model.Portal.ComputedURL</div>
</div>
<div class="row form-group">
<div class="col-md-1"><button id="btnConcatURL" asp-page-handler="ConcatURL" type="submit" class="btn btn-primary">Submit23</button></div>
</div>
</div>
</form>
<h3>Msg: #Model.Message</h3>
.cshtml.cs page:
public class VisibilityTestModel : PageModel {
[BindProperty]
public PortalInfo Portal { get; set; }
[TempData]
public string Message { get; set; }
public void OnGet() {
Portal = new PortalInfo {
Environment = "www.example.com",
Name = "test"
};
}
public void OnPostConcatURL() {
Portal.ComputedURL = Portal.Environment + " " + Portal.Name;
Message = "URL concat";
}
}
public class PortalInfo {
public string Environment { get; set; }
public string Name { get; set; }
public string ComputedURL { get; set; }
}

Thymeleaf bind a String field to a select box

#GetMapping("add")
public String addPart(Model model)
{
model.addAttribute("suppliers", this.partService.getSupplierNames());
model.addAttribute("part", new AddPartViewModel());
return "parts/parts-add";
}
This is my class
public class AddPartViewModel
{
private String name;
private double price;
private int quantity;
private String supplierName;
//PUBLIC GETERS AND SETTERS AND AN EMPTY CONSTRUCTOR
}
Thymeleaf syntax
<div class="form-group">
<label for="supplierName">Example select</label>
<select class="form-control" id="supplierName">
<option th:each="name : ${suppliers}" th:text="${name}" th:field="*{supplierName}"></option>
</select>
</div>
This is the only place where i get error on. The rest of the fragment works correctly, even if just remove the th:field tag the List<String> suppliers parces it self correctly in to the select box. Not i tried to put the th:field in the <select> tag as well, i.e
<select class="form-control" id="supplierName" th:field="*{supplierName}">
but still i get an error during parcing
th:field reffers to a form-backing bean's field, so make sure you've provided the proper bean in a <form> tag (using th:object attribute).
Regarding select: th:field should be provided in a <select> tag, like you've attempted to do. But you should provide also the proper th:value attribute in a <option> tag, so that any value could be assigned to the field.
Your form containing the problematic select should look like this:
<form th:object="${part}">
<div class="form-group">
<label for="supplierName">Example select</label>
<select class="form-control" th:field="*{supplierName}">
<option th:each="name : ${suppliers}" th:value="${name}" th:text="${name}"></option>
</select>
</div>
<!-- the rest of form's inputs and buttons -->
</form>

Restore the active bootstrap tab after postback when page has multiple forms

I have an ASP.Net MVC 5 project that has a page with 2 bootstrap tabs. Each tab has two forms for a total of 4 forms on the page. My viewmodel has a superset of all the fields in the 4 forms.
When I post a form, I want the response view to display the same tab that was showing when the form was posted. This SO answer Remain bootstrap tab after postback c# indicates that I should set the active tab in a hidden field, return it in the view model, then restore it on postback.
This SO answer asp.net MVC 4 multiple post via different forms indicates that returned fields must be within the form I'm returning. This presents a problem since the hidden field needs to be in the scope of all the forms.
On postback, how do I display the same tab that was showing when the form was posted when I have multiple forms on the page?
I solved this problem by removing the 4 forms and replacing it with a single form that spanned the previous 4. To get the form to post-back to the proper controller/action, I wrote some javascript that is invoked when one of the 4 submit buttons is activated.
It sets the new form's action to the appropriate controller/action and then submits the form. This way, there is only 1 form and 1 hidden field to hold the active tab and the correct action still gets invoked on post-back. A test program with 2 tabs and (for simplicity) only 2 actions is here:
View:
#model MultiPostDestinations.Models.HomeVM
#{
ViewBag.Title = "Home Page";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br/><br /><br /><br />
<div class="row">
<div class="col-md-4">
#using (Html.BeginForm("SubmitForm", "Home", FormMethod.Post, new { id = "submitForm", enctype = "multipart/form-data" })) {
#Html.HiddenFor(m => m.ActiveTab)
<div id="Tabs" role="tabpanel" class="container" style="width: 1000px">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="active">Details</li>
<li>Historic Model Analysis</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div id="details" role="tabpanel" class="tab-pane fade in active">
<h2>Details Tab</h2>
<h4>label: #Model.F1</h4><br /><br />
#Html.DisplayFor(m => m.F1)<br /><br />
#Html.EditorFor(m => m.F1)<br /><br />
#Html.ActionLink("ActionLink Submit form", "SubmitGet", "Home", new { #class = "btn btn-default" })<br /><br />
<input type="submit" value="input SubmitForm" class="btn btn-default" /><br /><br />
<input id="submitButton1" type="button" value="javascript input to /Home/Submit1" class="btn btn-default" /><br /><br />
</div>
<div id="history" role="tabpanel" class="tab-pane fade">
<h2>Historic Model Analysis tab</h2>
<h4>label: #Model.F2</h4><br /><br />
#Html.DisplayFor(m => m.F2)<br /><br />
#Html.EditorFor(m => m.F2)<br /><br />
<input type="submit" value="input SubmitForm" class="btn btn-default" /><br /><br />
<input id="submitButton2" type="button" value="javascript input to /Home/Submit2" class="btn btn-default" /><br /><br />
</div>
</div>
</div>
}
</div>
</div>
#section scripts {
<script type="text/javascript">
$(document).ready(function () {
$('#submitButton1').click(function (e) {
e.preventDefault(); // recommended in SO, does not appear to be required
$('#submitForm').attr('action', '/Home/Submit1');
$('#submitForm').submit();
});
$('#submitButton2').click(function (e) {
e.preventDefault(); // recommended in SO, does not appear to be required
$('#submitForm').attr('action', '/Home/Submit2');
$('#submitForm').submit(); // previous doesn't work - why?
});
var lastActiveTab = ($('#ActiveTab').val() !== '') ? $('#ActiveTab').val() : 'details';
$('#Tabs a[href="#' + lastActiveTab + '"]').tab('show');
$("#Tabs a").click(function () {
$('#ActiveTab').val($(this).attr("href").replace("#", ""));
});
});
</script>
}
Here is the Controller with 2 actions:
using MultiPostDestinations.Models;
using System.Web.Mvc;
namespace MultiPostDestinations.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
var vm = new HomeVM() { F1 = "Index-F1", F2 = "Index-F2", ActiveTab = "" };
return View("Index", vm);
}
[HttpPost]
public ActionResult Submit1(HomeVM vm) {
System.Diagnostics.Debug.WriteLine("HomeVM.Submit1: F1={0}, F2={1}", vm.F1 ?? string.Empty, vm.F2 ?? string.Empty);
// ModelState.Clear(); // uncomment if you want Html.EditorFor() fields to update on postback
return View("Index", vm);
}
[HttpPost]
public ActionResult Submit2(HomeVM vm) {
System.Diagnostics.Debug.WriteLine("HomeVM.Submit2: F1={0}, F2={1}", vm.F1 ?? string.Empty, vm.F2 ?? string.Empty);
//ModelState.Clear(); // uncomment if you want Html.EditorFor() fields to update on postback
return View("Index", vm);
}
}
}
And finally the view-model:
namespace MultiPostDestinations.Models {
public class HomeVM {
public string ActiveTab { get; set; }
public string F1 { get; set; }
public string F2 { get; set; }
}
}

Web API call from a ASP.NET MVC5 application

I have created a new web application with MVC template and core references having MVC and Web API.
I want to handle all the business logic through Web API controller. I have a login page view, where on form submit i want to make a call to the Web API method with the form data.
Login.cshtml
<form action="/api/Authorization/Login" method="post">
<div>
UserName: <input type="text" name="UserName" id="UserName" value="" />
</div>
<div>
Password: <input type="password" name="UserPassword" id="UserPassword" value="" />
</div>
<div>
<input type="submit" name="BtnSubmit" value="Login" />
</div>
I have added a Web API controller in the controller folder with the name 'Authorization' and on the above form submit, I'm not able to reach the method 'Login'.
Authorization Web API controller:
public class AuthorizationController : ApiController
{
public ActionResult Login(string UserName, string UserPassword)
{
// method logic
}
}
On form submit ending up with the following screen,
Please let me know what am i missing here so that i can reach the method defined under the api controller?
Also have not added any new route configs, the only config present in webApiConfig.cs is,
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You're missing a [HttpPost] attribute above your Login() function. As it is your method will only work for GET requests.
This page will help you out: https://www.asp.net/web-api/overview/advanced/sending-html-form-data-part-1

GET request: how to send query?

I would like to know what is the correct procedure for making a GET request inside the application code. I mean, not in the URL (is that a good practice?).
As always, I like to give you examples on what I'm looking for. So here it is:
HTML:
(...)
<div class="input-area">
<input type="text" value="SOME VALUE" id="text-field">
<input type="submit" value="Submit" id="button">
</div>
(...)
Let's assume that the #button action is "submitForm".
DART (it's incomplete):
void submitForm(Event e) {
e.preventDefault();
request = new HttpRequest();
request.onReadyStateChange.listen(dataIsReady);
request.open("GET", "http://127.0.0.1:8123");
(???)
}
What I would like to happen is the value on the text-field to be sent to the server as a parameter. Just to clarify, I'll be using that parameter to search for some entries in a MySQL database (and return some JSON data).
So how can I do that with Dart?
library x;
import 'dart:html';
void main() {
// you can create an URI and use the toString() method to create a
// String you can pass to the request
Map query = {
'xx': 'yy',
'zz': 'ss'
};
Uri uri =
new Uri(
path: "http://localhost:8080/myapp/signinService",
queryParameters: query);
print('URI: ${uri.toString()}');
querySelector("#signinForm")..onSubmit.listen(handle);
}
void handle(Event event) {
event.preventDefault();
// or you can use the form elements formdata
FormElement formElement = event.target as FormElement;
var url = "http://localhost:8080/myapp/signinService";
HttpRequest.request(
url,
method: formElement.method,
sendData: new FormData(formElement)).then(onDataLoaded);
}
void onDataLoaded(HttpRequest req) {
String response = req.responseText;
if (response == 1) {
window.alert("You are signed in!");
} else {
window.alert("Sign in failed. Check credentials.");
}
}
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="packages/browser/dart.js"></script>
</head>
<body>
<h1>Alarm</h1>
<!-- I used a form element and set the request method here. It can be set in code too -->
<form id="signinForm" class="form-signin" method="GET">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="text" class="input-block-level" name="email" placeholder="Email address">
<input type="password" class="input-block-level" name="password" placeholder="Password">
<button class="btn btn-large btn-primary" id="signinBtn" type="submit">Sign in</button>
</form>
<script type="application/dart" src="alarm.dart"></script>
</body>
</html>

Resources