Spring SimpleFormController in 3.0 - java

i notice that this controller has now been deprecated in the latest spring and was wondering what the alternative controller is?

In Spring 3.0 you should use simple classes annotated by #Controller. Such controller can handle more than one request. Each request is handled by its own method. These methods are annotated by #RequestMapping.
One thing you need to rethink is the fact, that a old school SimpleFormController handle a lot of different requests (at least: one to get the form and a second to submit the form). You have to handle this now by hand. But believe me it is easier.
For example this Controller in REST Style, will handle two requests:
/book - POST: to create a book
/book/form - GET: to get the form for creation
Java Code:
#RequestMapping("/book/**")
#Controller
public class BookController {
#RequestMapping(value = "/book", method = RequestMethod.POST)
public String create(
#ModelAttribute("bookCommand") final BookCommand bookCommand) {
Book book = createBookFromBookCommand(bookCommand);
return "redirect:/book/" + book.getId();
}
#RequestMapping(value = "/book/form", method = RequestMethod.GET)
public String createForm(final ModelMap modelMap) {
modelMap.addAttribute("all", "what you need");
return "book/create"; //book/create.jsp
}
}

Annotated POJOs can act as controllers; see #Controller.

In Spring 3.0, your Controllers should no longer inherit from a base class.
The standard way is to use annotated controllers.

Related

Spring boot - Combine DTO and form validation

I am currently developing an API where I'm using DTO for the first time. So far I've used Spring's form validation with javax.validation.
So my question is if there is a way to combine both DTO and "form" validation. Let me explain myself: lets say I have a service to log in and another to register. In the service to register we have: name, password and email, the 3 of them must be filled. As for the login service, only the email and password must be filled. So we'd have something like:
private String name;
private String password;
private String email;
Until now, what I did was to create a POJO per request (forms) and then use annotations such as #NotNull but now with DTO in the project I'm in now they just have the same DTO and business object with the same properties and no constraints.
How could I do what I was usually doing? Checking the fields that must be not null in the controller looks a little dirty to me and I can't just put something like #NotNull in the UserDTO because then in the two examples I said I'd have to send also the name when logging in although it's not needed for that service.
So, how could I combine these 2 things? Is this something not possible or there's a better approach?
Thanks.
I assume you are using two separate controllers for login and register requests.
And if it is the case, then you can make good use of org.springframework.validation.Validator interface:
#Component("registrationValidator")
public class RegistrationValidatorImpl implements Validator {
#Override
public boolean supports(final Class<?> aClass) {
}
#Override
public void validate(final Object o, final Errors errors) {
}
}
Create RegistrationValidatorImpl and LoginValidatorIml and #Autowire it in your controllers.
The usage of validator is simple:
invokeValidator(registrationValidator, someDTO, errors);
if (errors.hasErrors()) {
return new ResponseEntity(HttpStatus.BAD_REQUEST); //or whatever logic here
}
The controller method signature should be similar to this:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity register(#RequestBody final SomeDTO someDTO, final HttpServletRequest request, final Errors errors) {}
I case of one controller, I assume you have different methods mapped to login and register requests. You can #Autowire both validators in controller and use each in separate methods.
Using groups for validation with javax.validation did the work. I followed the answer in this question (as Andrew suggested), then I just had to put every field I wanted to have different rules in different groups.

Conflict in Using #RepositoryRestController and #RepositoryRestResource For a Single Resource in Spring Boot

I have a Payment entity in my spring boot application. Considering all possible CRUD operations, I'm using spring data rest for read and want to implement a custom create operation. Also delete and update are not allowed for this entity.
So this is my desired URLs and resoponsible component for each one:
GET /payments : PaymentRepository
GET /payments/{id} : PaymentRepository
POST /payments : PaymentController
This is my repository:
#RepositoryRestResource
public interface PaymentRepository extends PagingAndSortingRepository<Payment, Long> {
// disable create and update
#Override
#RestResource(exported = false)
Payment save(Payment entity);
// disable delete
#Override
#RestResource(exported = false)
void delete(Payment entity);
}
And this is my controller:
#RepositoryRestController
#RequestMapping("/payments")
public class PaymentController {
#PostMapping("")
#ResponseBody
public Payment create() {
// some code...
}
}
If I map create operation to a url like POST /payments/create, everything works fine, but If I use the above code and map create to POST /payments, the GET /payments url does not work any more and I get 405 Method Not Allowed error. (GET /payments/{id} is still working)
It seems in this case presence of #PostMapping("") annotation, cause the PaymentController to responsd the GET /payments request and it fails.
I hope my explanations were clear. How can I solve this problem?
The Spring Data REST reference states that:
Sometimes you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the #RepositoryRestController annotation instead of a standard Spring MVC #Controller or #RestController.
It is not explicitly mentionned, but annotating your controller with #RepositoryRestController also allows you to define a custom behavior for one endpoint while keeping all the other endpoints that Spring automatically generates... On one condition: the #RequestMapping annotation can only be used at the method level (this is actually what is done in the example of the reference documentation).
Your example becomes:
#RepositoryRestController
public class PaymentController {
#PostMapping("/payments")
#ResponseBody
public Payment create() {
// some code...
}
}
With this, you get your custom endpoint mapped to POST /payments requests, plus all endpoints automatically generated by Spring, minus the ones annotated with #RestResource(exported = false).
#BasePathAwareController
#RepositoryRestController
public class PaymentController {
#PostMapping("/payments")
#ResponseBody
public Payment create() {
// some code...
}
}
You should modify your controller in the above way. #BasePathAwareController enables the custom REST URI's to get registered under your base URI.
With the above modification : both API's can work fine.

Best Practice of redirecting in Spring Controller

Here I have to controller methods for example,
(Case 1) One way is
#Controller
#requestMapping("main")
Class ControllerClass{
#RequestMapping("first", method = RequestMethod.POST)
public String post(Model model){
//processing
return "redirect:second";
}
#RequestMapping("second", method = RequestMethod.GET)
public String post(Model model){
//processing
return "myview";
}
}
And (Case 2) another way is
#Controller
#requestMapping("main")
Class ControllerClass{
#RequestMapping("first", method = RequestMethod.POST)
public String post(Model model){
//processing
return "redirect:/main/second";
}
#RequestMapping("second", method = RequestMethod.GET)
public String post(Model model){
//processing
return "myview";
}
}
Both ways work correctly, but I want to know which one is better to avoid future problems like one I faced recently:
When I forwarded the request to /main/first from another controller, I got a 404 error in code which is using Case 1.
As per Spring Documentation:
The redirect: prefix
While the use of RedirectView works fine, if the controller itself creates the RedirectView, there is no avoiding the fact that the controller is aware that a redirection is happening. This is really suboptimal and couples things too tightly. The controller should not really care about how the response gets handled. In general it should operate only in terms of view names that have been injected into it.
The special redirect: prefix allows you to accomplish this. If a view name is returned that has the prefix redirect:, the UrlBasedViewResolver (and all subclasses) will recognize this as a special indication that a redirect is needed. The rest of the view name will be treated as the redirect URL.
The net effect is the same as if the controller had returned a RedirectView, but now the controller itself can simply operate in terms of logical view names. A logical view name such as redirect:/myapp/some/resource will redirect relative to the current Servlet context, while a name such as redirect:http://myhost.com/some/arbitrary/path will redirect to an absolute URL.
Most Real time enterprise project prefers to use case 2 in all controllers they use,so that the inter calling between different controller is fine.

Spring MVC Controller: Redirect without parameters being added to my url

I'm trying to redirect without parameters being added to my URL.
#Controller
...
public class SomeController
{
...
#RequestMapping("save/")
public String doSave(...)
{
...
return "redirect:/success/";
}
#RequestMapping("success/")
public String doSuccess(...)
{
...
return "success";
}
After a redirect my url looks always something like this: .../success/?param1=xxx&param2=xxx.
Since I want my URLs to be kind of RESTful and I never need the params after a redirect, I don't want them to be added on a redirect.
Any ideas how to get rid of them?
In Spring 3.1 a preferred way to control this behaviour is to add a RedirectAttributes parameter to your method:
#RequestMapping("save/")
public String doSave(..., RedirectAttributes ra)
{
...
return "redirect:/success/";
}
It disables addition of attributes by default and allows you to control which attributes to add explicitly.
In previous versions of Spring it was more complicated.
In Spring 3.1 use option ignoreDefaultModelOnRedirect to disable automatically adding model attributes to a redirect:
<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" />
Adding RedirectAttributes parameter doesn't work for me (may be because my HandlerInterceptorAdapter adds some stuff to model), but this approach does (thanks to #reallynic's comment):
#RequestMapping("save/")
public View doSave(...)
{
...
RedirectView redirect = new RedirectView("/success/");
redirect.setExposeModelAttributes(false);
return redirect;
}
In Spring 4 there is a way to do this with java config, using annotations. I'm sharing it in case anyone needs it as I needed it.
On the config class that extends WebMvcConfigurerAdapter, you need to add:
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void init() {
requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(true);
}
With this, you do not need to use RedirectAttributes, and it is an equivalent in java config to Matroskin's answer.
If you're using Spring 3.1, you can use Flash Scope,
otherwise you can take a look at the method used in the most voted (not accepted) answer here:
Spring MVC Controller redirect using URL parameters instead of in response
EDIT:
Nice article for 3.1 users:
http://www.tikalk.com/java/redirectattributes-new-feature-spring-mvc-31
Workaround for non-3.1 users:
Spring MVC custom scope bean
Try this:
public ModelAndView getRequest(HttpServletRequest req, Locale locale, Model model) {
***model.asMap().clear();*** // This clear parameters in url
final ModelAndView mav = new ModelAndView("redirect:/test");
return mav;
}

Aop Annotation at Spring Controllers Doesn't Work

I have made an annotation for aop. When I use it at any method rather than controller methods it works well. However when I use it at my controller's methods my controller stops working. It starts to give 404 not found error for mappings. I found a similar question here: Spring 3 MVC #Controller with AOP interceptors? but I don' know how to do it. My method at my controller is that:
#WebAuditable // This is my annotation that works at other methods
#Override
#RequestMapping(value = "/ad", method = RequestMethod.POST, headers = "Accept=application/json")
public
#ResponseBody
Cd create(HttpServletResponse response, #RequestBody Cd cd) {
...
}
My interface that my controller implements is that:
public interface BaseController<T> {
public List<T> getAll(HttpServletResponse response);
public T getByName(HttpServletResponse response, String id);
public T create(HttpServletResponse response, T t);
public T update(HttpServletResponse response, T t);
}
Any advices?
PS: #SeanPatrickFloyd says that:
Note When using controller interfaces (e.g. for AOP proxying), make
sure to consistently put all your mapping annotations - such as
#RequestMapping and #SessionAttributes - on the controller interface
rather than on the implementation class
The thing is: controller mapping is done at runtime, and if you use AOP proxies, the proxy objects don't have annotations at runtime, only their interfaces do. I can think of two possible strategies to work around this limitation.
Either annotate the generic interface methods, or (if you don't want to advise all controllers) create a sub-interface per implementation type, explicitly annotating their methods. I know that's a lot of rewritten code and contrary to what AOP is about, but I don't know a better way when sticking with interface based proxies.
Another way would be to switch to CGLib proxies using proxy-target-class="true". That way the proxy classes should (I'm not sure about this) retain the annotations.
Update: annotating your interface should work like this (if it works)
public interface BaseController<T> {
#WebAuditable
public List<T> getAll(HttpServletResponse response);
#WebAuditable
public T getByName(HttpServletResponse response, String id);
#WebAuditable
public T create(HttpServletResponse response, T t);
#WebAuditable
public T update(HttpServletResponse response, T t);
}
Annotating a base class won't work, because JDK proxies don't expose any information that's not backed by interfaces.

Categories