I am completely new to spring mvc. Please excuse me if this is asked already
Problem
A user can have multiple contact details. Each contact detail will have it's own form. For eg phone number -> 1 text box; address -> 3 text boxes
In PHP I used to do
<input type="text" name="contact[0][type]" value="PHONE">
<input type="text" name="contact[0][number]" value="456">
<input type="text" name="contact[1][type]" value="ADDRESS">
<input type="text" name="contact[1][address1]">
<input type="text" name="contact[1][address2]">
<input type="text" name="contact[1][address3]">
and my $_POST array will contain everything formatted into different associative arrays.
Can I do something similar in Spring ?
Update
I been on this for a couple of days and I have tried a few things including List, Map, Properties. I have also modified the name of the input fields. No luck.
For the domain Objects I successfully did the following
class User {
//..
Set<ContactDetails> details;
}
abstract ContactDetails{}
class Phone extends ContactDetails {}
class Address extends ContactDetails {}
Any help would be appreciated.
Related
So, this is my first post on stack, if I missed something please let me know.
So I have project that's working well on localhost Dev but when i uploaded to the server the API given 405 method not allowed.
I have a login form:
<form #submit.prevent="login" method="POST">
<input type="hidden" name="_token" :value="form.csrf">
<p><input type="email" name="login" placeholder="Username or Email" v-model="form.email"></p>
<p><input type="password" placeholder="Please Enter Password" v-model="form.password"></p>
<p class="remember_me">
</p>
<p class="submit"><input type="submit" value="Login"></p>
</form>
The form has some inputs, a textarea and a file input. Those are my routes:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::controller(AuthController::class)->group(function(){
Route::post('/login', 'login');
});
When I submit the form I get the The GET method is not supported for this route. Supported methods: POST. error.
Here is my Route list :
enter image description here
after everything possible things updating still on same error.
enter image description here
In Chrome network tab showing this
enter image description here
Maybe you guys have any idea what is happening here and point me in the right direction.
Thanks!
I tried using the Asp.Net Core TagHelper but it doesn't seem to work. However, when using HtmlHelpers it works as expected. My issue is that it always display the error message although the ModelState is valid. Am I doing something wrong or can someone reproduce this error?
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"> This field has an error. </span>
The property Firstname has a Required attribute in the ViewModel.
It works like this:
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
#Html.ValidationMessageFor(x => x.Firstname)
Edit:
It seems to work if I don't add the custom error message to the Html element but instead to the ViewModel DataAnnotation, like this:
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"></span>
Model:
[Required(ErrorMessage = "This field has an error.")]
public string Firstname { get; set; }
TL;DR:
Consider putting text inside the tag helpers in scenarios when you really want something
different from the generated value.
Full answer
You practically find the solution on your own, but I think I can still throw in my two cents here.
Most tag helpers work in a manner of generating content on a condition when its content is empty or contain only whitespace characters. For example, the ValidationMessageTagHelper checks it in this way:
var tagHelperContent = await output.GetChildContentAsync();
// We check for whitespace to detect scenarios such as:
// <span validation-for="Name">
// </span>
if (!tagHelperContent.IsEmptyOrWhiteSpace)
{
message = tagHelperContent.GetContent();
}
It gets tag content and then fills up message variable if the content is null, empty or whitespace. The message variable is then used to generate the validation message:
var tagBuilder = Generator.GenerateValidationMessage(
ViewContext,
For.ModelExplorer,
For.Name,
message: message,
tag: null,
htmlAttributes: htmlAttributes);
If the message is null or empty then the generator will provide the model error (see line 858 of DefaultHtmlGenerator);
if (!string.IsNullOrEmpty(message))
{
tagBuilder.InnerHtml.SetContent(message);
}
else if (modelError != null)
{
modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(
expression,
viewContext.ViewData,
_metadataProvider);
tagBuilder.InnerHtml.SetContent(
ValidationHelpers.GetModelErrorMessageOrDefault(modelError, entry, modelExplorer));
}
The GetModelErrorMessageOrDefault() of ValidationHelpers:
public static string GetModelErrorMessageOrDefault(
ModelError modelError,
ModelStateEntry containingEntry,
ModelExplorer modelExplorer)
{
Debug.Assert(modelError != null);
Debug.Assert(containingEntry != null);
Debug.Assert(modelExplorer != null);
if (!string.IsNullOrEmpty(modelError.ErrorMessage))
{
return modelError.ErrorMessage;
}
// Default in the ValidationMessage case is a fallback error message.
var attemptedValue = containingEntry.AttemptedValue ?? "null";
return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue);
}
So yes, if you put any text inside the <span> validation tag, the tag helper will choose your text over validation error from model state. Similar behaviour occurs if you put text inside the <label> tag as you did:
<label class="control-label" asp-for="Firstname">Firstname</label>
The tag helper will not overwrite the Firstname value you put inside the tag. It may not seem as bad behaviour, but if you would like to use display name for the Firstname property:
[Display(Name = "Fancy first name")]
public string Firstname { get; set; }
you would not see it work! Because the tag helper would again choose the text you put in-between <label> tags over the display name for Firstname.
What you should do is leave it as simple as i can be:
<label class="control-label" asp-for="Firstname"></label>
Consider putting text inside the tag helpers in scenarios when you really want something
different from the generated value.
At the begging I said that most tag helpers work that way. Most of them do, but not all of them. For example SelectTagHelper allows you to put any custom text inside the tag and if you provide a select list, it will generate the options by appending them to the existing content. It is extremely handy for adding custom <option> tags. For example I can easily add a selected and disabled option, so the dropdown does not have initial value, therefore the user is forced to manually select an option. These lines of code:
<select asp-for="LevelId" asp-items="#Model.Levels" class="custom-select">
<option selected disabled>Select option</option>
</select>
will result in:
<select class="custom-select" data-val="true" data-val-required="'Level Id' must not be empty." id="LevelId" name="LevelId">
<option selected disabled>Select parking level</option>
<option value="9">-2</option>
<option value="8">-1</option>
<option value="7">0</option>
</select>
I'd like to create the form for editing some Entity (for example a post) in the database using the Entity Framework Core.
I want to protect the value PostId in the hidden field before rewriting to another value from the browser. I'm wondering about checking the user permissions before updating but I want to create some encryption/signing or something like that.
How can I encrypt or sign the PostId and in the controller decrypt or validate it?
I've created the example form for editing the post like this:
Entity - Post:
public class Post
{
[Key]
public int PostId { get; set; }
[Required]
[StringLength(40)]
public string Title { get; set; }
}
Controller - PostsController with Edit method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("PostId,Title")] Post post)
{
if (ModelState.IsValid)
{
//Update method
}
return View(post);
}
Form for editing:
#model EFGetStarted.AspNetCore.NewDb.Models.Post
#{
ViewBag.Title = "Edit Post";
}
<h2>#ViewData["Title"]</h2>
<form asp-controller="Posts" asp-action="Edit" method="post" asp-antiforgery="true" class="form-horizontal" role="form">
<div class="form-horizontal">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="PostId" type="hidden" />
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" class="btn btn-default" />
</div>
</div>
</div>
</form>
By encrypting it you don't get any real business value and if the intent is so prevent one user to edit/modify posts he has no access to, you should do it in the backend by following the "Never trust the client" principle and always validate input on the server.
Easiest way to do is to use only the post ID from the model posted in and validate if the user has permissions to modify it. For this the new policy based systems offers resource based permissions which are well documented and can be used to validate the permissions.
Once done, passed take over the values and save the changes.
Also you shouldn't use persistence models inside the views, they easily break your API or your forms when the you change the database layout and navigation properties may cause issues (circular references etc.); especially later on, when lazy loading is implemented (lazy loading can't happen async as its inside a property, so the db call will block the thread).
Take a look at Sergey Akopov's blog post where he proposes a mechanism to deal with this scenario within ASP.NET MVC. His solution is to write a Html Helper that can be called within your view to generate a hidden input to accompany each input that you wish to make "tamper proof". This hidden input contains an encrypted copy of the value that you want to be tamper proof. When the form is posted, the server checks that the posted value and accompanying encrypted value still match - he writes a filter attribute which is applied to the corresponding controller action to perform this check. This adds an extra layer of "never trust the client" security.
Another example here has an interesting discussion (in the comments) around the potential security flaws inherent in this approach - The main one being that a determined attacker could "farm" valid combinations of secure field and encrypted value from their editing sessions, and subsequently use these farmed values to post tampered data with future edits.
I have something like this:
#Html.CheckBoxFor(model=>...)
<label>
#Html.DisplayFor(model=>...)
</label>
#Html.HiddenFor(model=>...)
But the final result in browser is:
1)Element
2)Hidden field
3)Label
I need to fix this because a library I use needs the label to be immediately after the Checkbox.
Why are you wrapping a <label> around a DisplayFor? Just use #Html.LabelFor.
Additionally. If you use the #Html.CheckBox.. helpers they will render a hidden field after the checkbox. There is a good reason for this, but requires a lengthy example.
What you probably want is an editor template. This should get you started:
#model Boolean
<input type="checkbox"
id="#ViewData.TemplateInfo.GetFullHtmlFieldId("")"
name="#ViewData.TemplateInfo.GetFullHtmlFieldName("")" />
The file for the code above is called "MyCheckbox" and is in Views/Shared/EditorTemplates
Model:
[UIHint("MyCheckbox")]
public bool MyProp { get; set; }
View:
#Html.EditorFor(x=> x.MyProp)
#Html.LabelFor(x=> x.MyProp)
Output:
<input type="checkbox" id="MyProp" name="MyProp">
<label for="MyProp">MyProp</label>
I have a site that has 2 forms - a short form and a long form. If you look at http://dforbesinsuranceagency.com you'll see the short form next to the masthead photo. The long form is at http://dforbesinsuranceagency.com/request-free-insurance-quotes/
When the user hits Submit on the short form, it kicks them over to the long form page, so that part works fine. The part that gives me fits is that I need the values entered into the short form fields First Name, Last Name, Email Address and Telephone passed to their equivalent fields on the long form.
How do I do this?
This is how I am redirecting the short form to the long form (I added it to the Additional Settings section for the short form):
on_sent_ok: "location = 'http://dforbesinsuranceagency.com//request-free-insurance-quotes';"
Any help would be appreciated.
Hack, hack, hackety, hack hack hack... Without suggesting "not using a form-builder" I don't think there is an elegant solution - you can't use the other PHP method suggested without modifying the plugin itself (and that is a can of worms). I will propose a Javascript solution but there are some caveats (below):
jQuery(document).ready(function($){
$('#quick-quote form:first').submit(function(){
var foo = {};
$(this).find('input[type=text], select').each(function(){
foo[$(this).attr('name')] = $(this).val();
});
document.cookie = 'formData='+JSON.stringify(foo);
});
var ff = $('#container form:first');
if(ff.length){
var data = $.parseJSON(
document.cookie.match('(^|;) ?formData=([^;]*)(;|$)')[2]
);
if(data){
for(var name in data){
ff.find('input[name='+name+'], select[name='+name+']').val(data[name]);
}
}
}
});
What this will essentially do is: on submission, store your mini-form options in a cookie. On page load it will then look for a form in the main body of the page and apply any stored cookie data.
Notes
The jQuery selectors are deliberately ambiguous to avoid any future changes in your admin panel/plugin that will likely screw with the form IDs (thus breaking the script).
I'm not faffing about pairing field/option names - for example the select box in your mini-form is named insurance-type however the matching box in the main form is named ins-type - you will have to ensure they are of the same name.
This also applies to select box values - if there is no matching value, it will be ignored (eg. some of your values in the main form have » » characters in front (and so don't match).
try this.
set the action of our first form to a php file named xyz.php
<form method="post" action="xyz.php">
<input type="text" name="name">
<input type="text" name="email_address">
<input type="submit" value="Go To Step 2">
</form>
the file xyz.php will create a new form for you which in this case is your second form (the big one). Set the action of the form as required. the code of your xyz.php will look something like this.
<form method="post" action="form3.php">
<input type="text" name="name" value="<?php echo $_POST['name']; ?>">
<input type="text" name="email_address" value="<?php echo $_POST['email_address']; ?>">
<input type="radio" group="membership_type" value="Free">
<input type="radio" group="membership_type" value="Normal">
<input type="radio" group="membership_type" value="Deluxe">
<input type="checkbox" name="terms_and_conditions">
<input type="submit" value="Go To Step 3">
</form>
where the input fields of the first form will already be filled with the details given by the user in the first form.
You can create the first form by yourself and let the contact form create the second form for you providing the default values using the method above.
Hope this helps!