Spring MVC Ajax Post 400 Error (Bad Request) - ajax

So I'm using Spring MVC and I'm trying to do an ajax post to add a comment to a Post entity, like a typical social network. And I'm getting an error in the Chrome Developer's Tool's that says this Failed to load resource: the server responded with a status of 400 (Bad Request). I'm thinking that might mean something is going wrong in my controller, however the way this is set up, it's not letting me check it out in debug mode.
I'll show you guys all the pieces of code that work together so you guys can get a better understanding of my problem.
So here's my Ajax, and everything is running through, and sending a "there was an error" message, so It's at least running through the code and reaching the controller.
Also the CDATA stuff is for Thymeleaf.
<script th:inline="javascript">
/*<![CDATA[*/
var postById = /*[[${postById.id}]]*/'1';
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
$(document).ready(function(){
$("#submit").on("click", function(ev) {
ev.preventDefault();
$.ajax({
url : "newComment",
type : "post",
data : {
"postById" : postById,
"newComment" : $("#newComment").val()
},
success : function(data) {
console.log(data);
location.reload();
},
error : function() {
console.log("There was an error");
//location.reload();
}
});
});
});
/*]]>*/
</script>
Here's my Controller Get Method
#RequestMapping(value="viewCourse/post/{postId}", method=RequestMethod.GET)
public ModelAndView postViewGet (#RequestParam(value = "pageSize", required = false) Integer pageSize,
#RequestParam(value = "page", required = false) Integer page, #PathVariable Long postId) {
ModelAndView modelAndView = new ModelAndView("post");
{
// Evaluate page size. If requested parameter is null, return initial
// page size
int evalPageSize = pageSize == null ? INITIAL_PAGE_SIZE : pageSize;
// Evaluate page. If requested parameter is null or less than 0 (to
// prevent exception), return initial size. Otherwise, return value of
// param. decreased by 1.
int evalPage = (page == null || page < 1) ? INITIAL_PAGE : page - 1;
//StudySet studySet = studySetRepo.findOne(studySetId);
//List <Row> rows = studySet.getRows();
//Set<Row> rowSet = new TreeSet<Row>(rows);
Post postById = postRepo.findOne(postId);
Comment comment = new Comment();
Page<Comment> postComments = commentService.findByPostOrderByIdDesc((postById), new PageRequest(evalPage, evalPageSize));
Pager pager = new Pager(postComments.getTotalPages(), postComments.getNumber(), BUTTONS_TO_SHOW);
modelAndView.addObject("postId", postId);
modelAndView.addObject("postById", postById);
modelAndView.addObject("postComments", postComments);
modelAndView.addObject("comment", comment);
modelAndView.addObject("selectedPageSize", evalPageSize);
modelAndView.addObject("pageSizes", PAGE_SIZES);
modelAndView.addObject("pager", pager);
return modelAndView;
}
}
Here's my Controller Post Method
#RequestMapping(value="viewCourse/post/newComment", method=RequestMethod.POST)
public #ResponseBody Post newComment (#Valid #RequestParam Long postId, #RequestParam String newComment, ModelMap model, #AuthenticationPrincipal User user)
{
Post post = postRepo.findOne(postId);
Comment comment = new Comment();
comment.setComment(newComment);
comment.setPost(post);
comment.setDate(LocalDate.now());
comment.setTime(LocalTime.now());
comment.setDateTime(LocalDateTime.now());
comment.setUser(user);
user.getComments().add(comment);
post.getComments().add(comment);
commentRepo.save(comment);
Post savedPost = postRepo.save(post);
return savedPost;
}
Also I have some annotations in the entity objects, that could have something to do with it.
Here's my User Entity
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
Here's my Comment entity
#ManyToOne
#JsonBackReference
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Also here's a picture of my console in the Chrome Developer Tools, so you guys can see exactly what it's showing me.
If anyone can see where I'm going wrong and point me in the right direction, that would be great, thanks in advance.
Also if you guys need to see any other code, just let me know.

My problem was in the Ajax, I was looking for the postById.id, when I should have been using just the postId that I added to the model in the Get method in the controller.

Related

How to show model attribute in JSP after using ajax?

I have a problem. I pass index value from JSP to controller successfully with ajax. When I click 'pass' button, the 'index' value is increasing and it passes to controller successfully with ajax. According to this index, I add list[index] to model.(with model.addAttribute) Although I have used ${nextWord} in the JSP, I cannot see this value in the view. How can I fix it? Thanks for your answer.
Controller
private List<Map<String, Object>> list;
#RequestMapping(value="/practice/{category}", method = RequestMethod.GET)
public String practicePageStart(#PathVariable("category") String category,
ModelMap model, HttpSession session){
// return 10 value from DB. Example;
// [{idWord=1},{word='exampleWord'},{meaning='turkishMeaning'},{type='NOUN'}]
list = wordService.getRandomWords(Integer.parseInt(String.valueOf(session.getAttribute("wordCount"))));
model.addAttribute("wordList", list);
return "practiceCategory";
}
#RequestMapping(value="/practice/{category}", method = RequestMethod.POST)
public String practicePagePost(#PathVariable("category") String category,
#RequestParam("index") int index, ModelMap model, HttpSession session){
model.addAttribute("nextWord", list.get(index).get("word"));
return "practiceCategory";
}
JSP
<script>
$(document).ready(function() {
$('#pass').click(function(event) {
var inputIndex = $('#index').val();
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath}/practice/${category}",
async: false,
data: { index: inputIndex }
complete: function(){
alert("${nextWord}");
$('#label').text("${nextWord}");
}
});
document.getElementById("index").value = (parseInt(document.getElementById("index").value) + 1).toString();
});
});
</script>
Change your controller method to this:
#RequestMapping(value="/practice/{category}", method = RequestMethod.POST)
#ResponseBody
public String practicePagePost(#PathVariable("category") String category,
#RequestParam("index") int index, ModelMap model, HttpSession session){
return list.get(index).get("word");
}
And your ajax to this:
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath}/practice/${category}",
async: false,
data: { index: inputIndex }
success: function(data){
alert(data);
$('#label').text(data);
}
});
Use #ResponseBody and return the object rather then returning a ViewResolver.
Returning a ViewResolver will resolve the view and send the html content while doing an Ajax call. Hence, it is not recommended if u need only value.
#ResponseBody example
public #ResponseBody Integer retriveValue(-,-,-){
return Integer.valueOf(5);
}
In my opinion you mix different:
(1) rendring phase (servlet container - background - java) vs.
(2) running in browser (js, no request attribute existing here).
You need one another jsp file just for rendering the data. Or you return it as json in practicePagePost method.
#ResponseBody
#RequestMapping(value="/practice/{category}", method = RequestMethod.POST)
public String practicePagePost(#PathVariable("category") String category,
#RequestParam("index") int index, ModelMap model, HttpSession session){
return list.get(index).get("word");
}

Ajax POST call to Spring MVC

This question is follow up of Returning ModelAndView in ajax spring mvc
As the only answer says that we need to return json from Controller not ModelAndView. So the question is
what can be done to return ModelAndView ?
How the page will be rendered:-
will it have to be handled in success section of ajax call
Or Spring Controller will return the page as usually it does in Spring MVC
How the post data from ajax can be read in Controller.
Update 1:
As explained, I tried example. here is my code.
#Controller
public class AppController
{
#RequestMapping(value="/greeting",method=RequestMethod.POST)
#ResponseBody
public ModelAndView getGreeting(#RequestBody String json) throws IOException
{
JSONObject inputjsonObject = new JSONObject(json);
String name = inputjsonObject.getString("name");
ModelAndView modelAndView = new ModelAndView();
String result = "Hi "+name;
modelAndView.addObject("testdata", result);
modelAndView.addObject("user", getPrincipal());
modelAndView.setViewName("greetingHtmlPage");
return modelAndView;
}
// other stuff
}
In above controller method i can get data sucessfully. This method is called from a javascript on home.html. Below is javascript function
function callGreeting(){
var nameData={
name : document.getElementById("name").value
}
var dataInJson = JSON.stringify(nameData);
var csrf_token = document.getElementById("token").value;
$.ajax({
type: 'POST',
url: "greeting",
data: dataInJson,
cache:false,
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token', csrf_token);
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Content-Type", "application/json");
},
success: function (response) {
document.open();
document.write(response);
document.close();
},
error: function (data) {
alert("failed response");
}
}); }
I have the page rendered successfully. But the url of application does not changes from AjaxSpringMVC:8080/home to AjaxSpringMVC:8080/greeting even after new page was loaded. This happens by itself in Spring MVC if using without Ajax.
what can be done to return ModelAndView ?
You can return ModelAndView As usual:
public ModelAndView returnView( Model model ) {
model.addAttribute( "myStaff", "my staff as string" );
return new ModelAndView( "myView" );
}
How the page will be rendered:
You control how it is rendered, .
When you return ModelAndView, the response has an HTML page.
After the Ajax call, you can do $("#container").html(response) or something like that to display the HTML page wherever you want.
In other words, you get a whole html page of content from the controller.
However, I highly recommend that you just return json object and update your view with the json object. Ajax is mostly used to create good user experience by updating part of the view asynchronously, so getting a whole page with Ajax does not make sense to me.
How the post data from ajax can be read in Controller.
There are many ways, I like to send json body directly to controller
#ResponseBody
#RequestMapping(value = "/saveObj", method = RequestMethod.POST, consumes = "application/json")
public String saveObj(Model model, #RequestBody MyObj myObj) {
// do staff..
}

SpringBoot/MVC & Thymleaf form validation on POST with URL parameters

I have a form and validation works. The problem comes in when a url parameter was added. The url parameter is a token and is required. So this is what my controller looks like:
#RequestMapping(value = "/resetpassword", method = RequestMethod.GET)
public String showResetForm(ResetPassword resetPassword, Model model,
#RequestParam(value = "token", required = true) String token,
#RequestParam(value = "msg", required = false) String msg){
model.addAttribute("token", token);
return "resetpassword";
}
#RequestMapping(value = "/resetpassword", method = RequestMethod.POST)
public String setPwd(#ModelAttribute("resetPassword") #Valid ResetPassword resetPassword,// RedirectAttributes reDirectAttr,
BindingResult bindingResult, Model model,
#RequestParam(value = "token", required = true) String token,
#RequestParam(value = "msg", required = false) String msg){
if (bindingResult.hasErrors()) {
//reDirectAttr.addFlashAttribute("org.springframework.validation.BindingResult.resetPassword",bindingResult);
//reDirectAttr.addFlashAttribute("resetPassword",resetPassword);
return "resetpassword?token="+token;
}
else {
if (token == null) {
// TODO: no token, what to do here??
return "redirect:/resetpassword?token=\"\"&msg=notoken";
}
ResetPasswordResponseDto response = super.resetUserPassword(
resetPassword.getUname(), resetPassword.getPassword(),
token);
if (response.getPasswordResetResult() == PasswordResetResult.SUCCESSFUL) {
// TODO: it worked, what now?
return "redirect:/login";
} else if (response.getPasswordResetResult() == PasswordResetResult.INVALID_TOKEN) {
// TODO: bad token
return "redirect:/resetpassword?token="+token+"&msg=badtoken";
} else if (response.getPasswordResetResult() == PasswordResetResult.OUT_OF_POLICY_PW) {
// TODO: out of policy pw
return "redirect:/resetpassword?token="+token+"&msg=outofpolicy";
} else if (response.getPasswordResetResult() == PasswordResetResult.LDAP_FAILURE) {
// TODO: other failure
return "redirect:/resetpassword?token="+token+"&msg=error";
}
}
return "redirect:/resetpassword?token="+token+"&msg=error";
//return new RedirectView("resetpassword?token=\"\"&msg=notoken");
}
So I tried a bunch of things but nothing seems to work. Here is what I would like to happen when the view is requested /resetpassword?token=1232453 the view is displayed. Then if the form has errors the url parameter persists in the url and the form displays the errors. Right now I get an error saying that the template cannot be resolved. Ok fair enough, so I tried doing a redirect instead
return "redirect:/resetpassword?token="+token;
and that seems to work, however the URL parameter is lost and the view loses the bindingResult errors. In the code, I posted I also tried FlashAttributes but I just get an error "Validation failed for object='resetPassword'. Error count: 4" which is correct but I need it to show the form and the errors I coded with Thymeleaf. Any help or suggestions would be great!
Resources I have looked at:
Spring - Redirect after POST (even with validation errors)
&
SpringMVC controller: how to stay on page if form validation error occurs
Have you tried returning a ModelAndView instead of just the redirect string? Attributes on the model will be available as URL query parameters.
ModelAndView redirect = new ModelAndView("redirect:/resetpassword");
redirect.addObject("token", token);
redirect.addObject("msg", "error");
return redirect;

Multiple form submition in spring mvc 3.0

i want to show entered data of user in a registration form (like preview page) to confirm correctness of entered data and if they accept, then that data should go into the database.
here is my controller code:
#RequestMapping( value="/catalogue/FormPreview.action", method=RequestMethod.POST)
public ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command,CatalogueBase catalogueBase) throws Exception {
if(catalogueBase.getTitleNumber()!= null)
{
request.setAttribute("titleNo", catalogueBase.getTitleNumber());
request.setAttribute("title", catalogueBase.getTitle());
request.setAttribute("name", catalogueBase.getName());
request.setAttribute("address", catalogueBase.getAddress());
request.setAttribute("email", catalogueBase.getEmail());
.....
return new ModelAndView("catalogue/catalogueFormPreview","catalogueBase",catalogueBase);
}
else
{
return create(catalogueBase);
}
}
#RequestMapping( value="/catalogue/create.action", method=RequestMethod.POST)
public ModelAndView create(#ModelAttribute CatalogueBase catalogueForm) throws Exception {
ModelAndView mvc = null;
try{
List<CatalogueBase> catalogueBases = new ArrayList<CatalogueBase>(); //getCatalogueBase(request);
catalogueBases.add(catalogueForm);
List<CatalogueBase> catalogueBaseList = catalogueService.create(catalogueBases);
mvc = new ModelAndView("catalogue/catalogueList");
} catch (Exception e) {
e.printStackTrace();
}
return mvc;
}
and I show the preview page as jsp using EL like:
Title NO : ${titleNo}
Title : ${title}
......
......
<a onclick="doAjaxPost();">Confirm Data<span class="icon icon44"></a>
and in the head section of the jsp I am calling ajax like:
<script>
function doAjaxPost() {
var name = $('#name').val();
var education = $('#education').val();
var str = $("#form").serialize();
$.ajax({
type: "POST",
url: "../catalogue/create.action",
data: str,
success: function(response){
alert("Record Added Successfully");
},
error: function(e){
alert('Error: ' + e);
}
});
};
it is showing data on preview page, but after clicking on confirm data, (hyperlink in preview page)
it sends null values to the create method(Second method) please can anyone tell why it's sending nulls and how I can solve this
thanks.
In Preview Page, you are only displaying the text, you need to get your data there as well in preview page either as hidden(or by any other means, like saving in session if much entries are there then etc). so that when you submit after confirmation, you can read all parameters.

httpmessagehandler - reading content

I created a message handler which will log the request and the response. ideally I want to
public class LoggingMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LogRequest(request);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
LogResponse(response);
return response;
});
}
private void LogRequest(HttpRequestMessage request)
{
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = request.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "request", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Message = x.Result;
});
});
}
private void LogResponse(HttpResponseMessage response)
{
var request = response.RequestMessage;
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = response.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "response", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Status = response.StatusCode;
t.Message = x.Result;
});
});
}
}
and here is my client code.
public ActionResult Index()
{
var profile = Client.GetAsync("Vendor").Result.EnsureSuccessStatusCode().Content.ReadAsAsync<VendorProfileModel>().Result;
return View(profile);
}
Logging appears to be working. However, when this handler is registered my client code returns an empty object. If I remove this handler the model is successfully read from the response and displayed on screen.
Is there a way to read the content and display the results on the client?
after a few more days for digging around on the net I finally found the root problem and a solution. First the problem:
everything in webapi is async
my action uses Controller.User which in turn is calling Thread.CurrentPrinciple
I am using ITraceWriter as my logging abstraction
apparently there is a bug in the ITraceWriter mechanicism where the current profile is not propagated across threads. therefore, i loose the principle when i get to my controller action. therefore, my query returns an empty result, rather than a fully populated result.
solution: don't use ITraceWriter to log messages. It would have been nice to use the built in mechanics, but that doesn't work. here is the link to the same issue which provides more detail/context.
https://aspnetwebstack.codeplex.com/workitem/237

Resources