I have an AuthenticationController working fine: all of its methods are running and I can see the logged output of methods. After successful Authentication I return a new modal like this:
modelAndView = new ModelAndView("redirect:/home/");
.....
return modelAndView;
I have another controller named HomePageController, but, after returning a model from Authentication, I am not able to get the code execution in any method of HomePageController.
What mappings do I need?
#Controller
#RequestMapping(value = "/home")
#SessionAttributes({"loginModel"})
public class HomePageController extends AbstractController {
Note: All methods in AuthenticationController are working fine..
web.xml file : http://snipt.org/vgEd7
mct-serverlet.xml file: http://snipt.org/vgEf2
replace trailing space
modelAndView = new ModelAndView("redirect:/home");
.....
return modelAndView;
That will look for /home/index.htm or something.
and in your HomepgageController, make sure there is some method, which returns view for /home url.
#Controller
#RequestMapping(value = "/home")
#SessionAttributes({"loginModel"})
public class HomePageController extends AbstractController {
public string handleHomePage(){
return "View Name";
}
}
Try this return statement : return new ModelAndView("redirect:home");
Avoid / in "redirect:/home/" after home action. It will look for index action automatically.
Related
I use Spring Data Rest and I can not understand why my RepositoryRestController does not work. Its code:
#RepositoryRestController
public class Cntrl {
#Autowired
private UserDao userDao;
#RequestMapping(name = "/users/{id}/nameOne",method =
RequestMethod.GET)
#ResponseBody
public PersistentEntityResource setNameOne(#PathVariable("id") Long id, PersistentEntityResourceAssembler persistentEntityResourceAssembler){
User user = userDao.findById(id).orElseThrow(()->{
throw new ServerException("Wrong id");
});
user.setLogin("One");
userDao.save(user);
return persistentEntityResourceAssembler.toFullResource(user);
}
}
And Spring Boot start class:
#SpringBootApplication
#EnableWebMvc
#EnableScheduling
#EnableJpaRepositories
#EnableSpringDataWebSupport
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
When i go to base path (localhost:8080/api) everything is fine, but when send GET to request to localhost:8080/api/users/1/nameOne I get empty response, i dont have other controllers and I have user with id 1, so why it is not working ?
It doesn't work because the URL structure you are using has already a meaning in Spring Data Rest context.
/{repository}/{id}/{column} URL is handled at RepositoryPropertyReferenceController.followPropertyReference method.
/api/users/1/nameOne means: get the nameOne column of the user with the id of 1. An important note is that: this column should reference another #Entity. This means if you have a String column named "surname" and you hit the URL /api/users/1/name you will get 404 because this column is not referencing another entity. If you have a column named school which references to a School entity and you hit the URL /api/users/1/school you will get the referenced school entity for that user. If the user does not have a school then you will get 404 again.
Also, #RepositoryRestController can be used for #RequestMapping if the URL you are giving isn't colliding with Spring Data Rest.
You can test that with the following example:
#RepositoryRestController
public class CustomRepositoryRestController {
#RequestMapping(path = "/repositoryRestControllerTest", method = RequestMethod.GET)
#ResponseBody
public String nameOne() {
return "test";
}
}
Visit http://localhost:8080/repositoryRestControllerTest
I hope this explanation clarifies things for you.
If localhost:8080/api is your root context, then localhost:8080/api/users/1/nameOne should be the url you are using for the user GET.
I am writing a Spring Boot application. I have written a simple controller that gets invoked whenever the endpoint is hit, but it still returns status 404 and not the specified return value.
HelloController
#Controller
public class MessageRequestController {
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
public String hello() {
System.out.println("Hit me!");
return "Hello, you!";
}
}
Now whenever I call localhost:8080/hello, I see the console log "Hit me!", but "Hello, you!" is never returned. Postman outputs:
{
"timestamp": 1516953147719,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/hello"
}
Application.java
#SpringBootApplication
#ComponentScan({"com.sergialmar.wschat"}) // this is the root package of everything
#EntityScan("com.sergialmar.wschat")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Change your method return a ResponseEntity<T>
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
public ResponseEntity<String> hello() {
System.out.println("Hit me!");
return new ResponseEntity<String>("Hello, you!", HttpStatus.OK);
}
or change the controller to RestController
#RestController
public class MessageRequestController {...}
CURL
ubuntu:~$ curl -X GET localhost:8080/hello
Hello, you!
Short version:
Annotate your endpoint method with ResponseBody to bind the return value to the response body.
#Controller
public class MessageRequestController {
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
#ResponseBody
public String hello() {
System.out.println("Hit me!");
return "Hello, you!";
}
}
You can instead annotate your class with RestController instead of Controller to apply ResponseBody to each method of the class.
#RestController
public class MessageRequestController {
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
public String hello() {
System.out.println("Hit me!");
return "Hello, you!";
}
}
With #Controller, you use the default model-view from Spring Web MVC, and you're actually telling spring to render the view called Hello, you!.tml from your resources directory (src/main/resources/templates for a Spring Boot project, if I remember correctly).
You can read this article for more information about the Spring MVC REST Workflow.
Once you're more familiar with those concepts, you can even further customize your endpoint method using ResponseEntity.
As you see the "hit me", there's no mapping issue, but in your #RequestMapping annotation you specifie a produce type to "application/json" and you return a simple poor String not formatted and without any header('Content-Type: application/json').
Add the header and format the outpout.
When everything seems ok but receive 404, check this answer:
As you know:
In the Spring MVC you can return view as a String or ModelAndView object.
IMPORTANT NOTE:
In both cases you have to pay attention to relative/absolute path:
If you declare / in the beginning of the view name, you are using absolute path.
Namely it does not matter class level #RequestMapping and directly introduce itself as the final view name.
If you do not declare / in the beginning of the view name, you are using relative path (relative to the class path) and therefore it appends to the class level #RequestMapping to construct final view name.
So, you have to consider the above notes when use the Spring MVC.
Example:
1. create two HTML file test1.html and test2.html in the static folder of spring (boot) structure:
Please note that the class level #RequestMapping behaves as a folder path in the case of relative path.
--- resources
--- static
--- classLevelPath //behaves as a folder when we use relative path scenario in view names
--- test2.html //this will be used for relative path [case (2)]
--- test1.html //this will be used for absolute path [case (1)]
create a controller class like as the below. This example shows different cases with return String and ModelAndView in both relative and absolute path.
#Controller
#RequestMapping("/classLevelPath")
public class TestController {
//case(1)
#RequestMapping("/methodLevelAbsolutePath1")
public String absolutePath1(Model model){
//model.addAttribute();
//...
return "/test1.html";
}
//case(1)
#RequestMapping("/methodLevelAbsolutePath2")
public ModelAndView absolutePath2(Model model){
ModelAndView modelAndView = new ModelAndView("/test1.html");
//modelAndView.addObject()
//....
return modelAndView;
}
//case(2)
#RequestMapping("/methodLevelRelativePath1")
public String relativePath1(Model model){
//model.addAttribute();
//...
return "test2.html";
}
//case(2)
#RequestMapping("/methodLevelRelativePath2")
public ModelAndView relativePath2(Model model){
ModelAndView modelAndView = new ModelAndView("test2.html");
//modelAndView.addObject()
//....
return modelAndView;
}
}
Note:
You can specify the suffix of your view files by a ViewResolver (for example InternalResourceViewResolver or spring.mvc.view.suffix=.html in the appliction.properties file of Spring Boot and do not declare .html suffix in the above code.
Best Regard
I am beginning with Spring Portlet MVC but I believe this question also applies to Spring MVC. Let me show you this example
#Controller
#RequestMapping(value="VIEW")//this maps to Portlet Mode VIEW
public class LoginController {
#RenderMapping("action=showRegisterPage")
//this annotation is almost the same like #RequestMapping
public String showRegisterPage() {
return "registration";
}
#ModelAttribute("loginForm")
public LoginForm getLoginForm() {
return new LoginForm();
}
}
and then controller for registration page
#Controller
#RequestMapping(value="VIEW")
public class RegistrationController {
#ModelAttribute("user")
public User getUser() {
return new User();
}
}
And the problem is, when I call the showRegisterPage method (action) from page I get redirected to registration.jsp but this exceptions occurs
Caused by: java.lang.IllegalStateException: Neither BindingResult nor
plain target object for bean name 'user' available as request attribute
This can be fixed by putting ModelAttribute user to LoginController but code duplication is not really right way, so how can I solve this correctly?I have also tried moving showRegisterPage method to RegistrationController but then the link is not working. Maybe somebody could elaborate how exactly binding of page to some controller works?
P.S. Both index.jsp and registration.jsp contains <form:form> tag and I also omitted boilerplate code from both controllers.
Exception is thrown because when you go to the registration page model doesn't have user attribute. I guess that your register page contains smth like this
<form:form commandName="user" >
That's why when you paste this code to first controller error is gone
#ModelAttribute("user")
public User getUser() {
return new User();
}
this code provide user object to model.
If you not want copy one method two times you can
#RenderMapping("action=showRegisterPage")
//this annotation is almost the same like #RequestMapping
public String showRegisterPage(Model model) {
model.addAttribute("user", new User());
return "registration";
}
In this case model will be contain user object.
Ideally the showregistrationpage method should b part of the registration controller rather than the login controller.
I would like to know how to read a flash attributes after redirection in Spring MVC 3.1.
I have the following code:
#Controller
#RequestMapping("/foo")
public class FooController {
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(...) {
// I want to see my flash attributes here!
}
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public ModelAndView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new ModelAndView().setViewName("redirect:/foo/bar");
}
}
What I am missing?
Use Model, it should have flash attributes prepopulated:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(Model model) {
String some = (String) model.asMap().get("some");
// do the job
}
or, alternatively, you can use RequestContextUtils#getInputFlashMap:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(HttpServletRequest request) {
Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
if (inputFlashMap != null) {
String some = (String) inputFlashMap.get("some");
// do the job
}
}
P.S. You can do return return new ModelAndView("redirect:/foo/bar"); in handlePost.
EDIT:
JavaDoc says:
A RedirectAttributes model is empty when the method is called and is
never used unless the method returns a redirect view name or a
RedirectView.
It doesn't mention ModelAndView, so maybe change handlePost to return "redirect:/foo/bar" string or RedirectView:
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public RedirectView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new RedirectView("/foo/bar", true);
}
I use RedirectAttributes in my code with RedirectView and model.asMap() method and it works OK.
Try this:
#Controller
public class FooController
{
#RequestMapping(value = "/foo")
public String handleFoo(RedirectAttributes redirectAttrs)
{
redirectAttrs.addFlashAttribute("some", "thing");
return "redirect:/bar";
}
#RequestMapping(value = "/bar")
public void handleBar(#ModelAttribute("some") String some)
{
System.out.println("some=" + some);
}
}
works in Spring MVC 3.2.2
For all those like me who were having problems with seeing the POST url in the browser when a validation would fail.
The POST url is a private url that should not be exposed to users but it was automatically rendered when a validation failed. i.e. if a field was below a minimum length. I was using #Valid. I wanted the original GET url of the form to show at all times even when validation bounced back to the form, so I did the following:
if (validation.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.story", validation);
redirectAttributes.addFlashAttribute("story", story);
return new ModelAndView("redirect:/january/2015");
where story is the form object representation, redirectAttributes are RedirectAttributes you put in the method signature and validation is the BindingResult. /january/2015 is the mapping to the GET controller where the form lives.
After this implementation, in the mapping for /january/2015, story comes in intact as follows:
Story story= (Story) model.asMap().get("story");
//story from the POST method
I had to augment my GET method and check if this was not null. If not null, then send this to the form else I would send a newly initialized Story type to the form as default behaviour before.
In this manner, I am able to return to the form with the bindingresults intact (errors show on form) but have my GET url in place of the post url.
How do I get a Spring 3.0 controller to trigger a 404?
I have a controller with #RequestMapping(value = "/**", method = RequestMethod.GET) and for some URLs accessing the controller, I want the container to come up with a 404.
Since Spring 3.0 you also can throw an Exception declared with #ResponseStatus annotation:
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
...
}
#Controller
public class SomeController {
#RequestMapping.....
public void handleCall() {
if (isFound()) {
// whatever
}
else {
throw new ResourceNotFoundException();
}
}
}
Starting from Spring 5.0, you don't necessarily need to create additional exceptions:
throw new ResponseStatusException(NOT_FOUND, "Unable to find resource");
Also, you can cover multiple scenarios with one, built-in exception and you have more control.
See more:
ResponseStatusException (javadoc)
https://www.baeldung.com/spring-response-status-exception
Rewrite your method signature so that it accepts HttpServletResponse as a parameter, so that you can call setStatus(int) on it.
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments
Since Spring 3.0.2 you can return ResponseEntity<T> as a result of the controller's method:
#RequestMapping.....
public ResponseEntity<Object> handleCall() {
if (isFound()) {
// do what you want
return new ResponseEntity<>(HttpStatus.OK);
}
else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
(ResponseEntity<T> is a more flexible than #ResponseBody annotation - see another question)
I would like to mention that there's exception (not only) for 404 by default provided by Spring. See Spring documentation for details. So if you do not need your own exception you can simply do this:
#RequestMapping(value = "/**", method = RequestMethod.GET)
public ModelAndView show() throws NoSuchRequestHandlingMethodException {
if(something == null)
throw new NoSuchRequestHandlingMethodException("show", YourClass.class);
...
}
you can use the #ControllerAdvice to handle your Exceptions ,
The default behavior the #ControllerAdvice annotated class will assist all known Controllers.
so it will be called when any Controller you have throws 404 error .
like the following :
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND) // 404
#ExceptionHandler(Exception.class)
public void handleNoTFound() {
// Nothing to do
}
}
and map this 404 response error in your web.xml , like the following :
<error-page>
<error-code>404</error-code>
<location>/Error404.html</location>
</error-page>
Hope that Helps .
While the marked answer is correct there is a way of achieving this without exceptions. The service is returning Optional<T> of the searched object and this is mapped to HttpStatus.OK if found and to 404 if empty.
#Controller
public class SomeController {
#RequestMapping.....
public ResponseEntity<Object> handleCall(#PathVariable String param) {
return service.find(param)
.map(result -> new ResponseEntity<>(result, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
#Service
public class Service{
public Optional<Object> find(String param){
if(!found()){
return Optional.empty();
}
...
return Optional.of(data);
}
}
If your controller method is for something like file handling then ResponseEntity is very handy:
#Controller
public class SomeController {
#RequestMapping.....
public ResponseEntity handleCall() {
if (isFound()) {
return new ResponseEntity(...);
}
else {
return new ResponseEntity(404);
}
}
}
I'd recommend throwing HttpClientErrorException, like this
#RequestMapping(value = "/sample/")
public void sample() {
if (somethingIsWrong()) {
throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
}
}
You must remember that this can be done only before anything is written to servlet output stream.
This is a bit late, but if you are using Spring Data REST then there is already org.springframework.data.rest.webmvc.ResourceNotFoundException
It also uses #ResponseStatus annotation. There is no need to create a custom runtime exception anymore.
Also if you want to return 404 status from your controller all you need is to do this
#RequestMapping(value = "/something", method = RequestMethod.POST)
#ResponseBody
public HttpStatus doSomething(#RequestBody String employeeId) {
try {
return HttpStatus.OK;
}
catch (Exception ex) {
return HttpStatus.NOT_FOUND;
}
}
By doing this you will receive a 404 error in case when you want to return a 404 from your controller.
Because it's always good to have at least ten ways of doing the same thing:
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class Something {
#RequestMapping("/path")
public ModelAndView somethingPath() {
return new ModelAndView("/", HttpStatus.NOT_FOUND);
}
}
Configure web.xml with setting
<error-page>
<error-code>500</error-code>
<location>/error/500</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error/404</location>
</error-page>
Create new controller
/**
* Error Controller. handles the calls for 404, 500 and 401 HTTP Status codes.
*/
#Controller
#RequestMapping(value = ErrorController.ERROR_URL, produces = MediaType.APPLICATION_XHTML_XML_VALUE)
public class ErrorController {
/**
* The constant ERROR_URL.
*/
public static final String ERROR_URL = "/error";
/**
* The constant TILE_ERROR.
*/
public static final String TILE_ERROR = "error.page";
/**
* Page Not Found.
*
* #return Home Page
*/
#RequestMapping(value = "/404", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
public ModelAndView notFound() {
ModelAndView model = new ModelAndView(TILE_ERROR);
model.addObject("message", "The page you requested could not be found. This location may not be current.");
return model;
}
/**
* Error page.
*
* #return the model and view
*/
#RequestMapping(value = "/500", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
public ModelAndView errorPage() {
ModelAndView model = new ModelAndView(TILE_ERROR);
model.addObject("message", "The page you requested could not be found. This location may not be current, due to the recent site redesign.");
return model;
}
}
Simply you can use web.xml to add error code and 404 error page. But make sure 404 error page must not locate under WEB-INF.
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
This is the simplest way to do it but this have some limitation. Suppose if you want to add the same style for this page that you added other pages. In this way you can't to that. You have to use the #ResponseStatus(value = HttpStatus.NOT_FOUND)