The situation is the following:
I have a JSP page with a form.
This form contains various <select> tags with options loaded from DB.
I want to use validation with an XML file.
The problem is the following: if I use an XML file and there are some errors in the form fields, the struts framework doesn't pass through the class method I laid out, but it will directly return the input result. So what's the point? That in this way I can't load the options for the various <select> tags I mentioned above.
So I thought to do something like this:
<result name="input" type="chain">
<param name="actionName">Class_method</param>
</result>
but with this trick I lose all the error messages, i.e. hasFieldErrors() returns always false.
How can I solve that?
Many questions, all good though.
Conversion and validation errors forces the Workflow interceptor to trigger the INPUT result, and the workflow will execute the INPUT result instead of reaching the action method (execute() or whatever).
If you need to populate some static data, like selectboxes sources, that must be available also in case of INPUT result, you should put that loading in a prepare() method, and make your action implement the Preparable interface. This method is run by an Interceptor before the INPUT result is returned, as described in the official docs.
Avoid using the chain result. It is officially discouraged since many years.
If you want to prevent double submits (by pressing F5 after a page has been submitted and the result rendered), you can use the PRG pattern with the redirectAction result. This way, however, you'd encounter the same problem of the chain result: the messages (and the parameters) will be lost.
To preserve the error messages, action errors and field errors across the redirections, you can use a predefined interceptor called Message Store Interceptor, that you must include in your stack because the defaultStack doesn't include it. I've described how it works in this answer.
If you decide to use the Message Store along with PRG there are more considerations, too long to be written here, but that could be explained in the future, about preventing infinite recursion due to Field Error -> INPUT -> PRG -> Retrieve Field Error -> INPUT -> etc... that will be blocked by the browser near the 10th recursion... but that's another story.
One option:
public class Foo extends ActionSupport {
public string myAction() { return SUCCESS; }
public void validateMyAction() { // executed after XML validation
// other complex validation here if needed
if (hasErrors()) {
// repopulate form data from DB here
}
}
}
hasErrors() method comes from the ValidationAware interface which ActionSupport implements.
Another option is to do a redirect on input result and use the message store interceptor to keep action messages
Related
For a Spring application I want to add a custom field to the log.
Currently I use the default format but I want to add a custom field (category field) which should be present in all the logs:
W:Action D:2022-01-10 23:21:03.285 L:INFO C:c.h.l.LoggingDemoApplication F:StartupInfoLogger.java(61) Fn:logStarted T:main R: - Hello World
What are the best solution to add a custom field to the logback log?
What I studied until now are the following possible solutions:
Use marker. The disadvantage with this is that it's not scalable: if in future you need another custom field can't add another marker. Further based on some other posts the marker is best suited to mark special logs that need to be handle differently.
Use MDC.
Also using this it seems not the best solution because:
It keeps the context so if there are multiple log statements in the same function, before each logger.info() there should be MDC.put("category", "action")
The code becomes to verbose.
Create a custom convertor (link). Get the arguments from the ILoggingEvent, get argument of 0. If this is the same type as category enum, then use it. The call for this is like logger.info("Message here: {} {} {}", CatEnum.Action.getValue(), msg1, msg2, msg3).
Create some static method in which the final format is generated.
Pattern is similar to: <pattern>%m%n</pattern>
To log, something like this should be used: logger.info(customFormatter.fmtLog(CatEnum.Action.getValue(), msg)). The returned value of fmtLog should be all the info from default logging + the category field.
Are there any built in solutions to add a custom field?
I have a service that saves a tree-like structure to a database. Before persisting the tree, the tree gets validated, and during validation, a number of things can go wrong. The tree can have duplicate nodes, or a node can be missing an important field (such as its abbreviation, full name, or level).
In order to communicate to the service what went wrong, I'm using exceptions. When the validateTree() method encounters a problem, it throws the appropriate exception. The HttpService class then uses this exception to form the appropriate response (e.g. in response to an AJAX call).
public class HttpService {
private Service service;
private Logger logger;
// ...
public HttpServiceResponse saveTree(Node root) {
try {
service.saveTree(root);
} catch (DuplicateNodeException e) {
return HttpServiceResponse.failure(DUPLICATE_NODE);
} catch (MissingAbbreviationException e) {
return HttpServiceResponse.failure(MISSING_ABBREV);
} catch (MissingNameException e) {
return HttpServiceResponse.failure(MISSING_NAME);
} catch (MissingLevelException e) {
return HttpServiceResponse.failure(MISSING_LEVEL);
} catch (Exception e) {
logger.log(e.getMessage(), e. Logger.ERROR);
return HttpServiceResponse.failure(INTERNAL_SERVER_ERROR);
}
}
}
public class Service {
private TreeDao dao;
public void saveTree(Node root)
throws DuplicateNodeException, MissingAbbreviationException, MissingNameException, MissingLevelException {
validateTree(root);
dao.saveTree(root);
}
private void validateTree(Node root)
throws DuplicateNodeException, MissingAbbreviationException, MissingNameException, MissingLevelException {
// validate and throw checked exceptions if needed
}
}
I want to know, is this a good use of exceptions? Essentially, I'm using them to convey error messages. An alternative would be for my saveTree() method to return an integer, and that integer would convey the error. But in order to do this, I would have to document what each return value means. That seems to be more in the style of C/C++ than Java. Is my current use of exceptions a good practice in Java? If not, what's the best alternative?
No, exceptions aren't a good fit for the validation you need to do here. You will likely want to display multiple validation error messages, so that the user can see all the validation errors at once, and throwing a separate exception for each invalid input won't allow that.
Instead create a list and put errors in it. Then you can show the user the list of all the validation errors.
Waiting until your request has gotten all the way to the DAO seems like the wrong time to do this validation. A server-side front controller should be doing validation on these items before they get passed along any farther, as protection against attacks such as injection or cross-site scripting.
TL;DR The Java-side parts you showed us are nearly perfect. But you could add an independent validation check and use that from the client side before trying to save.
There are many software layers involved, so let's have a look at each of them - there's no "one size fits all" answer here.
For the Service object, it's the perfect solution to have it throw exceptions from the saveTree() method if it wasn't able to save the tree (for whatever reason, not limited to validation). That's what exceptions are meant for: to communicate that some method couldn't do its job. And the Service object shouldn't rely on some external validation, but make sure itself that only valid data are saved.
The HttpService.saveTree() should also communicate to its caller if it couldn't save the tree (typically indicated by an exception from the Service). But as it's an HTTP service, it can't throw exceptions, but has to return a result code plus a text message, just the way you do it. This can never contain the full information from the Java exception, so it's a good decision that you log any unclear errors here (but you should make sure that the stack trace gets logged too!), before you pass an error result to the HTTP client.
The web client UI software should of course present detailed error lists to the user and not just a translated single exception. So, I'd create an HttpService.validateTree(...) method that returns a list of validation errors and call that from the client before trying to save. This gives you the additional possibility to check for validity independent of saving.
Why do it this way?
You never have control what happens in the client, inside some browser, you don't even know whether the request is coming from your app or from something like curl. So you can't rely on any validation that your JavaScript (?) application might implement. All of your service methods should reject invalid data, by doing the validation themselves.
Implementing the validation checks in a JavaScript client application still needs the same validation inside the Java service (see above), so you'd have to maintain two pieces of code in different languages doing exactly the same business logic - don't repeat yourself! Only if the additional roundtrip isn't tolerable, then I'd regard this an acceptable solution.
Visible and highly noticeable, both in terms of the message itself and how it indicates which dialogue element users must repair.
From Guru Nielsen,
https://www.nngroup.com/articles/error-message-guidelines/
I am using Play Framework with Java and with no prior experience with Scala. What I'm trying to achieve is to maintain request parameters (typically GET) across controller and view. More specifically, I need the view to pass the parameters sent to it by the controller (via the query string) back to the controller once it hands over control. A form is generated in the template using the form helper:
#form(routes.Application.authenticate())
I know I can access the current request with play.mvc.Controller.request(). I need to append the data submitted by the form to the current query string and pass it all via the URL, or, in case the form method is POST, either append the current query string to the action URL or store the parameters in hidden fields and pass them all through POST.
Is there a straightforward and clean way to ahieve this? At first I tried to pass everything via an object, but then I ran into trouble with the router, plus I couldn't figure out how to pass the data back.
UPDATE
This is usually can be done via hidden input field. According to this, there are 2 ways of rendering hidden input, but I prefer simplest one:
<input type="hidden" name="hiddenData" value="#data" />
On server side, you may get data from HashMap filledForm.data().get("hiddenData"), or via regular databind mechanism.
Since your data is undeterministic, you may change name and value on input control or pass HashMap as data. It will be rendered like this: value="{A=B, B=B}" and on server side you will receive HashMap.
To send request via GET you need to use controller accessed via GET at routes file like this:
#helper.form(routes.Application.doLoginGetMethod)
Generic forms
Play framework often lacks documentation for many important features, but at least it have examples in installation folder. %PLAYINSTALLFODLER%\samples\java\forms is what you need.
Documentation is here: Play framework Forms (Scala) Server side(Java), Templates
Anyway, idea is simple - you can use same form API for working with forms on client side and server side.
First create pojo to store data. Attributes needed for validation and visual tuning:
public static class Login
#Required
#Email
public String email;
#Required
#MinLength(5)
public String password;
}
Second, you need to create your form - it stateless, so can be reused. Pass it to your view:
public static final Form<Login> LOGIN_FORM = form(Login.class);
...
public static Result login() {
return ok(loginView.render(LOGIN_FORM));
}
On your template use helpers api from views.html.helper to render form and controls. There are plenty of them: checkbox, select, options, textarea and others.
#(loginForm: Form[_])
#import helper._
...
#helper.form(routes.Application.doLogin) { // this differ from original login method
#if(loginForm.hasGlobalErrors) { // example of validation and form data
<p class="error">
#loginForm.globalError.message</span>
</p>
}
#inputText( // Notice the helper
loginForm("email"),
'_showConstraints -> false,
'_label -> "Your email"
)
#inputPassword( // Another helper
loginForm("password"),
'_showConstraints -> true,
'_label -> "Password"
)
<input type="submit" value="Login"> // submit button
}
And then on server side receive form:
public static Result doLogin() {
final Form<Login> filledForm = LOGIN_FORM.bindFromRequest();
// User did not fill everything properly
if (filledForm.hasErrors()) return badRequest(login.render(filledForm));
return Controller.redirect("Hey!");
}
Of course you will need routes for this:
GET /login controllers.Application.login
POST /login controllers.Application.doLogin
With help from this and this I finally figured out how to generate the hidden input fields. Either of the following approaches does the job:
#for((key, value) <- request.queryString) {
<input type="hidden" name="#key" value="#value" />
}
Or:
#request.queryString.map { case (key,value) =>
<input type="hidden" name="#key" value="#value" />
}
In case of POST, #request.queryString can be simply replaced with #request.body.asFormUrlEncoded. Since both methods return Map[String, Seq[String]], one might want to flatten the values (using #value.mkString); however, in my case the code seems to work fine as is. My ignorance about Scala prevents me from delving deeper into what's happening under the hood, but I'm guessing that in each iteration, the first element from the array is returned, which should work as far as HTTP request parameters in my application are concerned. If I ever test this with edge cases, I will update this post.
I have been wrestling with this problem for a while. I would like to use the same Stripes ActionBean for show and update actions. However, I have not been able to figure out how to do this in a clean way that allows reliable binding, validation, and verification of object ownership by the current user.
For example, lets say our action bean takes a postingId. The posting belongs to a user, which is logged in. We might have something like this:
#UrlBinding("/posting/{postingId}")
#RolesAllowed({ "USER" })
public class PostingActionBean extends BaseActionBean
Now, for the show action, we could define:
private int postingId; // assume the parameter in #UrlBinding above was renamed
private Posting posting;
And now use #After(stages = LifecycleStage.BindingAndValidation) to fetch the Posting. Our #After function can verify that the currently logged in user owns the posting. We must use #After, not #Before, because the postingId won't have been bound to the parameter before hand.
However, for an update function, you want to bind the Posting object to the Posting variable using #Before, not #After, so that the returned form entries get applied on top of the existing Posting object, instead of onto an empty stub.
A custom TypeConverter<T> would work well here, but because the session isn't available from the TypeConverter interface, its difficult to validate ownership of the object during binding.
The only solution I can see is to use two separate action beans, one for show, and one for update. If you do this however, the <stripes:form> tag and its downstream tags won't correctly populate the values of the form, because the beanclass or action tags must map back to the same ActionBean.
As far as I can see, the Stripes model only holds together when manipulating simple (none POJO) parameters. In any other case, you seem to run into a catch-22 of binding your object from your data store and overwriting it with updates sent from the client.
I've got to be missing something. What is the best practice from experienced Stripes users?
In my opinion, authorisation is orthogonal to object hydration. By this, I mean that you should separate the concerns of object hydration (in this case, using a postingId and turning it into a Posting) away from determining whether a user has authorisation to perform operations on that object (like show, update, delete, etc.,).
For object hydration, I use a TypeConverter<T>, and I hydrate the object without regard to the session user. Then inside my ActionBean I have a guard around the setter, thus...
public void setPosting(Posting posting) {
if (accessible(posting)) this.posting = posting;
}
where accessible(posting) looks something like this...
private boolean accessible(Posting posting) {
return authorisationChecker.isAuthorised(whoAmI(), posting);
}
Then your show() event method would look like this...
public Resolution show() {
if (posting == null) return NOT_FOUND;
return new ForwardResolution("/WEB-INF/jsp/posting.jsp");
}
Separately, when I use Stripes I often have multiple events (like "show", or "update") within the same Stripes ActionBean. For me it makes sense to group operations (verbs) around a related noun.
Using clean URLs, your ActionBean annotations would look like this...
#UrlBinding("/posting/{$event}/{posting}")
#RolesAllowed({ "USER" })
public class PostingActionBean extends BaseActionBean
...where {$event} is the name of your event method (i.e. "show" or "update"). Note that I am using {posting}, and not {postingId}.
For completeness, here is what your update() event method might look like...
public Resolution update() {
if (posting == null) throw new UnauthorisedAccessException();
postingService.saveOrUpdate(posting);
message("posting.save.confirmation");
return new RedirectResolution(PostingsAction.class);
}
Does Wicket somehow allow passing both of the following kinds of params in a PageParameters object? Apparently not?
accountId which is shown in the URL (/account/<ID>)
infoMessage parameter which is not shown in the (bookmarkable) URL
I'm currently using IndexedHybridUrlCodingStrategy for the page in question, and simply trying parameters "0" and "infoMessage" gives this exception:
WicketMessage: Not all parameters were encoded. Make sure all
parameter names are integers in consecutive order starting with zero.
Current parameter names are: [0, infoMessage]
If I change "infoMessage" parameter name into "1", it works, but yields an ugly URL (in this case something like /account/42/Tosite%20108207%20tallennettiin.5) which is not what I want.
Now, the obvious answer perhaps is that infoMessage shouldn't be in PageParameters. But thing is, I tried adding it as normal constructor parameter instead, like so:
public AccountPage(PageParameters parameters, String infoMessage) {
// ...
}
But this approach fails in one important use case. After deleting a persistent "Record" object related to the Account, the following does not load the AccountPage properly (the deleted record is still visible). This code is executed in onClick() of an AjaxFallbackLink.
setResponsePage(new AccountPage(AccountPage.pageParameters(account), message));
On the other hand, my original approach...
setResponsePage(AccountPage.class, AccountPage.pageParameters(account));
... works fine, as it somehow loads the AccountPage "more thoroughly", but, again, I don't know how to pass the infoMessage parameter cleanly.
(AccountPage.pageParameters() above is a simple static utility for creating appropriate PageParameters with "0" = account id. The AccountPage constructor always loads the account from persistence using the ID.)
Any ideas? Perhaps using AjaxFallbackLink partially causes the problem?
Using Wicket 1.4.
From what I see in your question, you try to render both a bookmarkable page and show a feedback message to the user (most probably in a FeedbackPanel), but you don't want that message to be part of the URL.
What you want to do is tell the Session that you have an informational message, and let the feedback panel handle the message.
#Override void onSubmit() {
... save object ...
getSession().info("Object ... has been saved");
setResponsePage(ObjectPage.class, new PageParameters("id="+object.getId()));
}
In this case you tell Wicket to temporarily store a message in the session, until it gets rendered by a feedback panel. This idiom is also known as "flash messages".
You can't use both PageParameters and another parameter as constructor arguments, because Wicket can't create your page instance with such a constructor when the page is requested. Wicket only knows how to instantiate pages with default constructors or pages with a PageParameters parameter.