I need your help!
In my web project (it based on Spring MVC) i'm using exceptions to indicate that some validation is failed, but i'm not sure that doing the right way.
For example i have such service:
#Service
#Transactional
public class UserService {
#Autowired
private UserRepository userRepository;
public User createUser(UserDTO userDTO) throws IllegalArgumentExceptio {
validateUserEmail(userDTO);
return userRepository.save(new User(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(), userDTO.getPassword()));
}
private void validateUserEmail(UserDTO userDTO) throws IllegalArgumentException {
String emailPattern = "^[a-z0-9-\\+]+(\\.[a-z0-9-]+)*#"
+ "[a-z0-9-]+(\\.[a-z0-9]+)*(\\.[a-z]{2,})$";
if (userDTO() == null) {
throw new IllegalArgumentException(INVALID_EMAIL_NULL.getMessage());
} else if (userDTO().length() > 25) {
throw new IllegalArgumentException(INVALID_EMAIL_LENGTH.getMessage());
} else if (!userDTO().matches(emailPattern)) {
throw new IllegalArgumentException(INVALID_EMAIL_FORMAT.getMessage());
}
}
}
I've read this one article. Also i know that there is another one approach is to use Hibernate Validator.
So, the main question is: which one approach is the best practice and why?
Throw an exception during validation as i do.
Use something like notification pattern.
Use Hibernate Validator.
Clearly, using the Hibernate Validator for Bean Validations, which is the readily available library is the best approach as we don't need to rewrite huge code for min length/max length, etc.. validations explicitly.
Also, if you rewrite the logic for minlength/maxlength validations ourself, you need to do the extensive testing to ensure that the written code is correct, which is must.
The thumb rule, if some trusted code is already available, do not try to reinvent/rewrite again, rather just make use of it, which is called DRY (Don't Repeat Yourself) principle, very important in any programming
That's a very bad idea. One of issues with your solution that comes to my mind is like: How you are going to notify a user if your email validation fails? I would go for custom validation annotation. If validation of email fails, you simply return false. With that approach, you can also easy notify user if email validation fails because simply, BindingResult will contain errors. Also, another benefit is localization of error message. So yeah, Hibernate validatior is way to go.
Related
I am building a REST-API with SpringBoot and using this Controller.
#RestController
class EmployeeController {
private final EmployeeRepository repository;
EmployeeController(EmployeeRepository repository) {
this.repository = repository;
}
#GetMapping("/employees")
List<Employee> all() {
return repository.findAll();
}
#PostMapping("/employees")
Employee newEmployee(#RequestBody Employee newEmployee) {
return repository.save(newEmployee);
}
I want to ensure that API-Consumers cannot spam multiple concurrent POST-Requests with the same Employee. I know that I can check if the entity already exists in the database before saving it, but I am afraid that the performance will be bad. I also already noticed that you can use Annotation like #version in your entity, to make updates on existing Entity`s more save.
But is there also a way or a best practice in Spring how to handle this POST-Requests with a potential new Entity?
What kind of request throughput are you expecting to the POST /employees endpoint? While performance is important, premature optimization is almost always going to cause your code to be uglier than it needs to for little gain.
As your code currently stands, multiple concurrent POST /employees requests would end up with a first-come first-serve basis where the first user with the given UNIQUE constraint in your application (which is hopefully enforced by your underlying DBMS) is created, and all other after that (for the same user) would fail due to a e.g. ConstraintViolationException (mapped to a e.g. DataIntegrityViolationException). From this point of view (as long as you do not have a complicated distributed DBMS setup), the consistency of the data is still guaranteed.
The downside, of course, is that the error messages that would be returned would be:
Vendor-specific and leak the underlying implementation (e.g. we're showing the client that we're using Hibernate)
Potentially difficult for the client to parse.
If you instead, change the implementation to something like the following:
#PostMapping("/employees")
Employee newEmployee(#RequestBody Employee newEmployee) {
verifyEmployeeDoesNotExist(newEmployee);
return repository.save(newEmployee);
}
private void verifyUserDoesNotExist(Employee employee) {
if (repository.exists(newEmployee) {
throw new EmployeeAlreadyExistsException("Employee " + newEmployee.getName() + " already exists";
}
}
then you could more easily control the control flow of your endpoint and the underlying process, which would potentially allow for more easily-digestible exception handling. This could be even further improved by adding e.g. custom exceptions which also contain some code of pre-defined error code such as e.g. error 409 code 1010 Employee already exists.
Of course, Spring's built-in exception translation for Hibernate (e.g. HibernateExceptionTranslator) might already be good enough for your use-case and could even be extended and this extension can be even generalized.
In the end, the best practice is making your code clean, readable and maintainable. Then start adding functionality to monitor your code. After that, and only if you have a problem with performance, you can still optimize it.
Can you change a method's signature in Spring using aspects?
Like effectively transform the following:
#GetMapping("/thing")
#User // custom annotation that should authenticate the user
public ResponseEntity getThing() {
... // user is successfully authenticated, get the "thing" from the database
}
into:
#GetMapping("/thing")
public ResponseEntity getThing(#CookieValue("Session-Token") String sessionToken) {
User user = authenticator.authenticateSessionTokenOrThrow(sessionToken);
... // user is successfully authenticated, get the "thing" from the database
}
With the user variable also becoming available for use in the method body.
If not, how can I achieve the same result without repeating the code (parameter and authenticator call) everywhere?
Aspects aren't meant for that.
Yes, they can effectively modify .class files bytecode, with compile time or run time weaving, but they do not override methods' signatures.
Also, the default Spring AOP Aspects are implemented in pure Java, and thus cannot touch the bytecode layer. For that you'd need AspectJ.
Tools for customizing bytecode at run/compile time are ASM, ByteBuddy, CGLIB or Javassist.
However, you can probably accomplish this via an Annotation Processor, which lets you modify the actual sources, instead of the already compiled bytecode.
If not, how can I achieve the same result without repeating the code
(parameter and authenticator call) everywhere?
Possible solutions are
HandlerInterceptor, which simply throws an Exception if the user isn't authenticated
Standard Spring AOP advice, which simply throws an Exception if the user isn't authenticated
Spring Security
1 is pretty easy.
2 is more time-consuming
3 imho, seems the best match for authentication, but it's the most complex, probably
The HandlerInterceptor can choose which methods it applies to?
No, unfortunately. I had a requirement a couple of months ago to "cover" only certain methods with an Interceptor, and I implemented a custom solution, which simply look for an annotation specified on the method itself.
This is an extract of my custom HandlerInterceptor, which looks for the CheckInit annotation, first on the type, and then on the method, for a more specific customization.
#Override
public boolean preHandle(
final HttpServletRequest request,
final HttpServletResponse response,
final Object handler
) throws Exception {
if (handler instanceof HandlerMethod) {
if (shouldCheckInit((HandlerMethod) handler)) {
checkInit();
}
}
return true;
}
private static boolean shouldCheckInit(final HandlerMethod handlerMethod) {
final var typeAnnotation = handlerMethod.getBeanType().getAnnotation(CheckInit.class);
final var shouldCheckInit = typeAnnotation != null && typeAnnotation.value();
final var methodAnnotation = handlerMethod.getMethodAnnotation(CheckInit.class);
return (methodAnnotation == null || methodAnnotation.value()) && shouldCheckInit;
}
private void checkInit() throws Exception {
if (!manager.isActive()) {
throw new NotInitializedException();
}
}
The "Standard Spring AOP advice" seems interesting, do you have a link
for that?
Spring AOP documentation - look for the Java-based configuration (I hate XML)
AspectJ really touches the bytecode and can modify signatures as well?
You could make AspectJ modify signatures. Just fork the project and modify its Java Agent or compiler.
AFAIK Annotation Processors cannot modify classes, they can only
create new ones.
The thing is, they don't modify .class files, instead they modify source files, which means they simply edit them. E.g. Lombok uses annotation processing to modify source files.
But yes, the modified sources are written to a new file.
Suppose I've got an endpoint in Dropwizard, say
#GET
public Response foo() { throw new NullPointerException(); }
When I hit this endpoint it logs the exception and everything, which is great! I love it. What I love less is that it returns a big status object to the user with status: ERROR (which is fine) as well as a gigantic stack trace, which I'm less excited about.
Obviously it's best to catch and deal with exceptions on my own, but from time to time they're going to slip through. Writing a try catch block around the entire resource every time is fine, but (a) it's cumbersome, and (b) I always prefer automated solutions to "you have to remember" solutions.
So what I would like is something that does the following:
Logs the stack trace (I use slf4j but I assume it would work for whatever)
Returns a general purpose error response, which does not expose potentially privileged information about my server!
I feel like there must be a built-in way to do this -- it already handles exceptions in a relatively nice way -- but searching the docs hasn't turned up anything. Is there a good solution for this?
As alluded to by reek in the comments, the answer is an ExceptionMapper. You'll need a class like this:
#Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
#Override
public Response toResponse(RuntimeException runtime) {
// ...
}
}
You can do whatever logging or etc. you like in the toResponse method, and the return value is what is actually sent up to the requester. This way you have complete control, and should set up sane defaults -- remember this is for errors that slip through, not for errors you actually expect to see! This is also a good time to set up different behaviors depending on what kind of exceptions you're getting.
To actually make this do anything, simply insert the following line (or similar) in the run method of your main dropwizard application:
environment.jersey().register(new RuntimeExceptionMapper());
where environment is the Environment parameter to the Application's run method. Now when you have an uncaught RuntimeException somewhere, this will trigger, rather than whatever dropwizard was doing before.
NB: this is still not an excuse not to catch and deal with your exceptions carefully!
Add the following to your yaml file. Note that it will remove all the default exception mappers that dropwizard adds.
server:
registerDefaultExceptionMappers: false
Write a custom exception mapper as below:
public class CustomExceptionMapper implements ExceptionMapper<RuntimeException> {
#Override
public Response toResponse(RuntimeException runtime) {
// ...
}
}
Then register the exception mapper in jersey:
environment.jersey().register(new CustomExceptionMapper());
Already mentioned this under the comments, but then thought I would give it a try with a use case.
Would suggest you to start differentiating the Exception that you would be throwing. Use custom exception for the failures you know and throw those with pretty logging. At the same RuntimeException should actually be fixed. Anyhow if you don't want to display stack trace to the end user you can probably catch a generic exception, log the details and customize the Response and entity accordingly.
You can define a
public class ErrorResponse {
private int code;
private String message;
public ErrorResponse() {
}
public ErrorResponse(int code, String message) {
this.code = code;
this.message = message;
}
... setters and getters
}
and then within you resource code you can modify the method as -
#GET
public Response foo() {
try {
...
return Response.status(HttpStatus.SC_OK).entity(response).build();
} catch (CustomBadRequestException ce) {
log.error(ce.printStackTrace());
return Response.status(HttpStatus.SC_BAD_REQUEST).entity(new ErrorResponse(HttpStatus.SC_BAD_REQUEST, ce.getMessage())).build();
} catch (Exception e) {
log.error(e.printStackTrace(e));
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage())).build();
}
}
This article details Checked and Unchecked Exceptions implementation for Jersey with customized ExceptionMapper:
https://www.codepedia.org/ama/error-handling-in-rest-api-with-jersey/
Official Dropwizard documentation also covers a simpler approach, just catching using WebApplicationException:
#GET
#Path("/{collection}")
public Saying reduceCols(#PathParam("collection") String collection) {
if (!collectionMap.containsKey(collection)) {
final String msg = String.format("Collection %s does not exist", collection);
throw new WebApplicationException(msg, Status.NOT_FOUND)
}
// ...
}
https://www.dropwizard.io/en/stable/manual/core.html#responses
It worked for me by simply registering the custom exception mapper created in the run method of the main class.
environment.jersey().register(new CustomExceptionMapper());
where CustomExceptionMapper can implement ExceptionMapper class like this
public class CustomExceptionMapperimplements ExceptionMapper<Exception>
I have made simple application for study perpose and i want to write some unit/intagration tests. I read some information about that i can mock data base insted of create new db for tests. I will copy the code which a write. I hope that some one will explain me how to mock database.
public class UserServiceImpl implements UserService {
#Autowired
private UserOptionsDao uod;
#Override
public User getUser(int id) throws Exception {
if (id < 1) {
throw new InvalidParameterException();
}
return uod.getUser(id);
}
#Override
public User changeUserEmail(int id, String email) {
if (id < 1) {
throw new InvalidParameterException();
}
String[] emailParts = email.split("#");
if (emailParts[0].length() < 5) {
throw new InvalidParameterException();
} else if (!emailParts[1].equals("email.com")) {
throw new InvalidParameterException();
}
return uod.changeUserEmail(id, email);
}
This above i a part of the code that i want to test with the mock data base.
Generally you have three options:
Mock the data returned by UserOptionsDao as #Betlista suggested, thus creating a "fake" DAO object.
Use an in-memory database like HSQLDB to create a database with mock data when the test starts, or
Use something like a Docker container to spin up an instance of MySQL or the like and populate it with data, so you can restart it as necessary.
None of these solutions are perfect.
With #1, your test will skip the intermediate steps of authenticating to the database and looking for data. That leaves a part of your code untested, and as they say, "the devil is in the details." Often people run into problems when they mock DAO's like this when they try to deploy.
With #2, you connect to an actual database, but you have to make sure that either you are using the exact same type of database in your production code or something compatible. It also makes debugging a pain because you have to pause the test to see the contents of the database if something goes wrong.
With #3, you avoid all the problems with #1 and #2, but then you have to wire up all the Docker stuff. (I'm doing this right now, and I'm having problems too). The advantage, though, is that like #2 you can set up all of your test data at once, and be guaranteed that the production database you choose will be exactly the same as your unit test.
In your case, I would go with #2 since the application is for study purposes. Yes, I know this is a long-winded answer, but as you gain experience, you will probably want to know how to "scale up."
What you can do very easily is to have your implementation of UserOptionsDao in test package and set this one to UserServiceImpl. This new implementation can return fixed set of data for example...
This is a highlevel idea. You probably do not want to have many implementations (different for each test in general), so you should use some mocking framework like Mockito or EasyMock, look at the documentation for more details.
We all know, that Spring MVC integrate well with Hibernate Validator and JSR-303 in general. But Hibernate Validator, as someone said, is something for Bean Validation only, which means that more complex validations should be pushed to the data layer. Examples of such validations: business key uniqueness, intra-records dependence (which is usually something pointing at DB design problems, but we all live in an imperfect world). Even simple validations like string field length may be driven by some DB value, which makes Hibernate Validator unusable.
So my question is, is there something Spring or Hibernate or JSR offers to perform such complex validations? Is there some established pattern or technology piece to perform such a validation in a standard Controller-Service-Repository setup based on Spring and Hibernate?
UPDATE: Let me be more specific. For example, there's a form which sends an AJAX save request to the controller's save method. If some validation error occurs -- either simple or "complex" -- we should get back to the browser with some json indicating a problematic field and associated error. For simple errors I can extract the field (if any) and error message from BindingResult. What infrastructure (maybe specific, not ad-hoc exceptions?) would you propose for "complex" errors? Using exception handler doesn't seem like a good idea to me, because separating single process of validation between save method and #ExceptionHandler makes things intricate. Currently I use some ad-hoc exception (like, ValidationException):
public #ResponseBody Result save(#Valid Entity entity, BindingResult errors) {
Result r = new Result();
if (errors.hasErrors()) {
r.setStatus(Result.VALIDATION_ERROR);
// ...
} else {
try {
dao.save(entity);
r.setStatus(Result.SUCCESS);
} except (ValidationException e) {
r.setStatus(Result.VALIDATION_ERROR);
r.setText(e.getMessage());
}
}
return r;
}
Can you offer some more optimal approach?
Yes, there is the good old established Java pattern of Exception throwing.
Spring MVC integrates it pretty well (for code examples, you can directly skip to the second part of my answer).
What you call "complex validations" are in fact exceptions : business key unicity error, low layer or DB errors, etc.
Reminder : what is validation in Spring MVC ?
Validation should happen on the presentation layer. It is basically about validating submitted form fields.
We could classify them into two kinds :
1) Light validation (with JSR-303/Hibernate validation) : checking that a submitted field has a given #Size/#Length, that it is #NotNull or #NotEmpty/#NotBlank, checking that it has an #Email format, etc.
2) Heavy validation, or complex validation are more about particular cases of field validations, such as cross-field validation :
Example 1 : The form has fieldA, fieldB and fieldC. Individually, each field can be empty, but at least one of them must not be empty.
Example 2 : if userAge field has a value under 18, responsibleUser field must not be null and responsibleUser's age must be over 21.
These validations can be implemented with Spring Validator implementations, or custom annotations/constraints.
Now I understand that with all these validation facilites, plus the fact that Spring is not intrusive at all and lets you do anything you want (for better or for worse), one can be tempted to use the "validation hammer" for anything vaguely related to error handling.
And it would work : with validation only, you check every possible problem in your validators/annotations (and hardly throw any exception in lower layers). It is bad, because you pray that you thought about all the cases. You don't leverage Java exceptions that would allow you to simplify your logic and reduce the chance of making a mistake by forgetting to check that something had an error.
So in the Spring MVC world, one should not mistake validation (that is to say, UI validation) for lower layer exceptions, such has Service exceptions or DB exceptions (key unicity, etc.).
How to handle exceptions in Spring MVC in a handy way ?
Some people think "Oh god, so in my controller I would have to check all possible checked exceptions one by one, and think about a message error for each of them ? NO WAY !". I am one of those people. :-)
For most of the cases, just use some generic checked exception class that all your exceptions would extend. Then simply handle it in your Spring MVC controller with #ExceptionHandler and a generic error message.
Code example :
public class MyAppTechnicalException extends Exception { ... }
and
#Controller
public class MyController {
...
#RequestMapping(...)
public void createMyObject(...) throws MyAppTechnicalException {
...
someServiceThanCanThrowMyAppTechnicalException.create(...);
...
}
...
#ExceptionHandler(MyAppTechnicalException.class)
public String handleMyAppTechnicalException(MyAppTechnicalException e, Model model) {
// Compute your generic error message/code with e.
// Or just use a generic error/code, in which case you can remove e from the parameters
String genericErrorMessage = "Some technical exception has occured blah blah blah" ;
// There are many other ways to pass an error to the view, but you get the idea
model.addAttribute("myErrors", genericErrorMessage);
return "myView";
}
}
Simple, quick, easy and clean !
For those times when you need to display error messages for some specific exceptions, or when you cannot have a generic top-level exception because of a poorly designed legacy system you cannot modify, just add other #ExceptionHandlers.
Another trick : for less cluttered code, you can process multiple exceptions with
#ExceptionHandler({MyException1.class, MyException2.class, ...})
public String yourMethod(Exception e, Model model) {
...
}
Bottom line : when to use validation ? when to use exceptions ?
Errors from the UI = validation = validation facilities (JSR-303 annotations, custom annotations, Spring validator)
Errors from lower layers = exceptions
When I say "Errors from the UI", I mean "the user entered something wrong in his form".
References :
Passing errors back to the view from the service layer
Very informative blog post about bean validation