Spring redirect in controller not working like return "redirect:/reservation/reservationSuccess" but return "/reservation/reservationSuccess"; is working. Why it is not working. where it went wrong. Please help.
#RequestMapping(method = RequestMethod.POST)
public String submitForm(#ModelAttribute("reservation") Reservation reservation,
BindingResult result,
SessionStatus status,
Model model) {
validator.validate(reservation, result);
if(result.hasErrors()) {
model.addAttribute("reservation",reservation);
return "reservation/reservationForm";
}
reservationService.make(reservation);
status.setComplete();
return "redirect:reservation/reservationSuccess";
}
When you're doing a redirect to reservation/reservationSuccess, by definition, the browser will send a new request to the URL reservation/reservationSuccess of your web app. You will see the complete URL in the address bar of your browser.
If this URL is not mapped to any servlet in your web app, you will obviously get a 404 error.
You need to understand that the point of a redirect is not to dispatch to a view (a JSP). The point is to make the browser go to another URL in your web app. The path you put after the redirect: prefix is thus supposed to be the path of an action of your Spring MVC app. Not the path of a view.
You have to have another method in your Controller to intercept the reservation/reservationSuccess GET request.
For example:
#RequestMapping(value="reservation/reservationSuccess", method = RequestMethod.GET)
public String getSuccess() {
return "reservation/reservationForm";
}
"redirect:xxx" is looking for a RequestMapping to match the redirect string xxx, however, the return "xxx" is going to look for View Resolver to map that string to a JSP page. That is the main difference.
Related
I was searching around but I couldn't find a working answer for my issue. I saw a similar question but we had different results. My issue is I have 2 controllers. The first controller has a POST action that I want to return a CreatedAtRoute to a GET action inside a different controller. I tested the GET action in Postman and it works so my only issue is getting the CreatedAtRoute to work.
My first controller:
[HttpPost]
public async Task<IActionResult> Submit(AssessmentAttemptVM attempt)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
//Do database related stuff
await _context.SaveChangesAsync();
return CreatedAtRoute("GetAssessmentResult", new { id = studentAttempt.Id }, studentAttempt);
}
My second controller:
[HttpGet("{id}", Name = "GetAssessmentResult")]
public async Task<ActionResult<AssessmentResultVM>> GetById(int id)
{
//Get my ViewModel -- This works if I try to access it without using the CreatedAtRoute method
return resultVM;
}
The picture below shows what Postman responds with when I try to Post. I verified that the data gets added to the database so only the CreatedAtRoute method is the only I can think of that isn't making this work for me..
EDIT
Controller Route Attributes:
[ApiController]
[Route("api/view/assessmentresult/")]
public class AssessmentResultsController: ControllerBase
{
[ApiController]
[Route("api/take/assessment")]
public class StudentAssessmentController : ControllerBase
{
I found the cause. The last parameter for CreatedAtRoute and CreatedAtAction required an object similar to the destination controller. It went over my head because I was sending models prior to what I did now which used a ViewModel.
Well That wasn't the main reason I couldn't get a response though. It was because of an execption where the object I'm passing ended up having recursive references because I was sending a model that wasn't properly formatted to be sent over as JSON.
I used this to make it work, taken from the MS Docs site:
CreatedAtAction(String, String, Object, Object) Where the last parameter should be the object you want to the api to send over.
PS: I also didn't notice immediately because when I debugged the project, it didn't crash and had to read the logs. I'm a noob so I really didn't know that it's possible for an exception to occur without the project crashing in debug mode.
I have integrated payment gateway now, on success URL I want to pass some data from one controller to another controller but it's not working properly it's showing null value sometimes so, What I have to use instead of Session or TempData.
public void Index(UserRegistreModel model)
{
TempData["model2"]= model;
redirecturl += "&return=" + ConfigurationManager.AppSettings["SuccessURL"].ToString();
}
public ActionResult AnotherControllerMethod(UserRegistreModel model)
{
UserRegistreModel add = (UserRegistreModel) TempData["model2"];
//not getting any values
}
Your are adding User type class to Temp Data but you are extracting UserRegistreModel type so, that's why this was empty, Use like this :-
public void Index(User model)
{
TempData["model2"]= model;
redirecturl += "&return=" +
ConfigurationManager.AppSettings["SuccessURL"].ToString();
}
public ActionResult AnotherControllerMethod(User model)
{
User add = (User) TempData["model2"];
//not getting any values
}
How u are Passing Data ? Is it a forward or redirect? Forward will take the current data of your Request Object as it is the same Request but ur URL will not be changed in client.
And If u are redirecting then its a new Request. In This case the response will go to the client first (browser) then come to your new controller. As it is a new request, it will not have the old data from your calling controller.
Spring Has a special type of pojo called RedirectAttributes for this. Where you can add FlashAttribute for this type of requirement. Check the similar thing in .net
I have an ASP.NET MVC4 front end as one project in my solution, and a separate ASP.NET Web API as another project in the same solution. The Web API will contain all of my CRUD operations.
2 questions
How do I call my Web API from my front end to perform CRUD operations? I have my entity data model defined in my Web API project, and I will need to bind my front end views to it, how would I do that?
Once this is deployed to my web servers, the front end will reside on one server, and the Web API will reside on another server (the server that holds most of our web services). So, I guess along the same lines, how would I call the Web API from my front end once deployed? I understand Web API's are simply called with an HTTP request, but in terms of passing my models (which are defined in my Web API project) into my Views (in my front end project), how can I do this?
While Kevin is right, I did this the non-Ajax way. Keep in mind that I am working with JSON data, so this is centered around JSON.
In your controller page, remove anything that has to do with DbContext, Entity Framework, etc. The reason is by default, the controller will want to perform CRUD operations by calling the DbContext, and we don't want this. We want to call the WebAPI instead to do this.
First and foremost, declare some member variables in your controller. The rest of your controller will utilize these:
HttpClient client = new HttpClient();
HttpResponseMessage response = new HttpResponseMessage();
Uri contactUri = null;
In your controller, create a constructor for your controller, as such:
public ContactController()
{
// set base address of WebAPI depending on your current environment
client.BaseAddress = new Uri("http://server/YourAPI/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
Replace the Index action's code with something like the following. Note that the only relevant pieces are the client.GetAsync() call and the var contacts assignment. Everything else is not necessary for the context of this problem. The value inside the client.GetAsync() should be the name of your controller, prepended by any custom routing you set up in your WebApiConfig.cs - in my case, I added the api part in my route to distinguish between API calls and normal calls:
public ActionResult Index()
{
response = client.GetAsync("api/contact").Result;
if (response.IsSuccessStatusCode)
{
var contacts = response.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
return View(contacts);
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Create action (the HttpPost action) with something like the following. Again, the only important piece is the client.PostAsJsonAsync() part - this is what calls the WebAPI's POST action which takes care of, in my case, inserting a new record into the database:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Contact contact)
{
// Create a new product
response = client.PostAsJsonAsync("api/contact", contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Edit action (the non-HttpPost action) with something like the following. This was a little tricky because in order to edit, you had to retrieve the record first, so basically, the HttpPost version of Edit will contain somewhat similar code, with an additional line of code that performs the edit POST (PUT). Below, we're getting the response from the WebAPI by passing it a specific record ID. So, just like for Index (GET), we are doing the same thing only passing in the ID so we only get back one record. Then, we cast the response to an actual object that can be operated on in the View:
public ActionResult Edit(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Replace the Edit action (the HttpPost action) with something like the following. Below, we're getting the record to be edited by calling client.GetAsync() and passing in the primary key as a parameter (contact_id). Then, we're getting the RequestUri from that response and saving it. Then, we're calling client.PutAsJsonAsync() and passing in the Uri.PathAndQuery (what we just saved) as well as the object to be edited.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Contact contact)
{
response = client.GetAsync(string.Format("api/contact/{0}", contact.contact_id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.PutAsJsonAsync(contactUri.PathAndQuery, contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Delete action (the non-HttpPost action) with something like the following. So again, we're getting the record from the database by simply calling client.GetAsync() and casting it to an actual object my app knows of.
public ActionResult Delete(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Finally, replace the Delete action (the HttpPost action) with something like the following. Again, we're doing something similar to that of the Edit action. We are getting the record to be deleted, casting it to an object, and then passing that object into a client.DeleteAsync() call, as shown below.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.DeleteAsync(contactUri).Result;
return RedirectToAction("Index");
}
You can call your Web API from the client using jQuery ajax method. But since you are calling from a site other than where the Web API is deployed you will have to use JSONP, instead of JSON. Take a look at this QA to see how you use JSONP with Web API. Your models will be passed as JSON which you will have to render on the client side, instead of using Razor to render it on the server side. I would use something like Knockout to create a View Model on the client that will bind your model to the HTML elements on the client.
I'm trying to forward a request to another Spring controller that takes a GET request, but it's telling me POST is not supported. Here is the relevant portion from my first controller method, which does take a POST request, since I'm using it for a login function.
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#ModelAttribute("administrator") Administrator administrator,
Model model) {
// code that's not germane to this problem
return "forward:waitingBulletins";
}
Here is the method I'm trying to forward to.
#RequestMapping(value = "/waitingBulletins", method = RequestMethod.GET)
public String getWaitingBulletins(Model model) {
// the actual code follows
}
Here is the error message in my browser.
HTTP Status 405 - Request method 'POST' not supported
--------------------------------------------------------------------------------
type Status report
message Request method 'POST' not supported
description The specified HTTP method is not allowed for the requested resource (Request method 'POST' not supported).
forward maintains the original request intact, so you are forwarding a POST request and missing a handler for it.
By the looks of it, what you're really trying to implement is the POST-redirect-GET pattern, which uses redirect instead of forward.
You only need to change your POST handler to:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#ModelAttribute("administrator") Administrator administrator,
Model model) {
// code that's not germane to this problem
return "redirect:waitingBulletins";
}
to make it work.
I am very confused using spring web MVC to "redirect".
I want to redirect to a URL, but the address bar in brwser is not updated.
First, I post a form to a controller and in this controller I use the "redirect:" prefix to a new controller (it works), but the address bar is still referencing the previous URL.
Here is my code for teh controller to receive the post request:
#RequestMapping(method=RequestMethod.POST)
public ModelAndView processSubmit(LoginFormBean formBean, BindingResult result,
Model model) {
System.out.println(formBean.getUsername());
System.out.println(formBean.getPassword());
return new ModelAndView("redirect:/index");
}
For the controller to redirect:
#Controller
#RequestMapping("/index")
public class IndexController {
#RequestMapping(method=RequestMethod.GET)
public String show() {
return "index";
}
}
Before the form is posted, the form, URL is "http://localhost:7001/mobi"
after post and redirect, the URL is not "http://localhost:7001/mobi/index", but still "http://localhost:7001/mobi"
Are there any visible error in my code that need to be corrected?
Can the community assist me in implementing this correctly?
The only visible answer I can surmise based on your code is that its doubtful you have a modelandview that's a redirect to index. You can pretty much ditch the modelandview syntax with Spring 3, and just return the pages you want directly. Here's an example from my own code, where I also receive a posted form and redirect to another controller:
#RequestMapping(value = REQUEST_MAP, method = RequestMethod.POST)
public String processForm(HttpServletRequest req, HttpServletResponse res, #ModelAttribute("myForm") myForm form, BindingResult result)
throws Exception {
//super cool form handling logic here!
return "redirect:/anotherControllerMappedURI";
}
I could have also just returned a string to return a view page directly. Like, return "index" to send the user to an index.jsp page. Then you could use a meta tag to do an html redirect if necessary.