So, I'm trying to to a POST request to a local Spring MVC project, but every time I try, I keep getting 400: Bad Request, with the error specified in the title. Not sure why, because when I check what's being sent, it's correct.
Spring REST controller request
#RequestMapping(value="/add", method = RequestMethod.POST)
void addPet(#RequestParam String name, #RequestParam String photo, #RequestParam String status) {
Pet pet = new Pet(name, photo, status);
this.petRepo.save(pet);
}
AngularJS request sending the data
$http({
method: "post",
url: "http://localhost:8080/pet/add",
data:{
name: angular.element("#name")[0].value,
photo: angular.element("#photo")[0].value,
status: angular.element("#status")[0].value
}
}).success(function(data, status) {
})
.error(function(data, status) {
alert("Error");
console.log(data);
console.log(status);
});
front-end HTML that's calling the AngularJS function
<div class="form-group">
<label for="name">Pet Name</label>
<input type="text" class="form-control" id="name">
</div>
<div class="form-group">
<label for="photo">Photo URL</label>
<input type="text" class="form-control" id="photo">
</div>
<div class="form-group">
<label for="status">Status</label>
<input type="text" class="form-control" id="status" placeholder="Something descriptive like 'Available' or 'Taken'">
</div>
<div class="form-group">
<div class="btn-group">
<button type="button" class="btn btn-primary" ng-click="preview()">Preview</button>
<button type="button" class="btn btn-success" ng-click="submit()">Submit</button>
<button type="button" class="btn btn-danger" ng-click="clear()">Clear</button>
</div>
</div>
You are using #RequestParam in the backend but in angular you are putting the values in the body of the request.
You have to use #RequestBody in the backend or adjust the frontend to put the values in url parameters.
Found the answer. I followed SSH's answer above for the front-end, and for the backend, I changed the function to:
void addPet(#RequestBody Pet pet)
try like this
<div class="form-group">
<label for="name">Pet Name</label>
<input type="text" class="form-control" ng-model="form.name" id="name">
</div>
<div class="form-group">
<label for="photo">Photo URL</label>
<input type="text" class="form-control" ng-model="form.photo" id="photo">
</div>
<div class="form-group">
<label for="status">Status</label>
<input type="text" class="form-control" ng-model="form.status" id="status" placeholder="Something descriptive like 'Available' or 'Taken'">
</div>
<div class="form-group">
<div class="btn-group">
<button type="button" class="btn btn-primary" ng-click="preview()">Preview</button>
<button type="button" class="btn btn-success" ng-click="submit(form)">Submit</button>
<button type="button" class="btn btn-danger" ng-click="clear()">Clear</button>
</div>
</div>
and in controller use this
$scope.form = {"name":"","photo":"","status":""}
$http({
method: "post",
url: "http://localhost:8080/pet/add",
data:{
name: $scope.form.name,
photo: $scope.form.photo,
status: $scope.form.status
}
}).success(function(data, status) {
})
.error(function(data, status) {
alert("Error");
console.log(data);
console.log(status);
});
Related
I am trying to allow currently logged in user of my spring application update their current details but it is not persisting to the database, I am getting no errors and have tried to debug but have gotten no success.. please take a look.
Service class:
#Transactional
public User updateAccount(User userInForm){
System.out.println("Fetching user with id: " + userInForm.getId());
Optional<User> optionalUser = repo.findById(userInForm.getId());
if(!optionalUser.isPresent()){
System.out.println("User not found.");
return null;
}
User userInDB = optionalUser.get();
System.out.println("User fetched: " + userInDB);
userInDB.setFirstName(userInForm.getFirstName());
userInDB.setLastName(userInForm.getLastName());
System.out.println("Saving updated user: " + userInDB);
User savedUser = repo.save(userInDB);
System.out.println("User saved: " + savedUser);
return savedUser;
}
Controller class:
#PostMapping("/myAccount/update")
public String updateAccount(User user, RedirectAttributes redirectAttributes, Principal principal){
System.out.println("Updating user details...");
user = repo.findByEmail(principal.getName());
User updatedUser = service.updateAccount(user);
if (updatedUser == null) {
System.out.println("Error updating user details.");
} else {
redirectAttributes.addFlashAttribute("message", "Details Updated!");
return "redirect:/myAccount";
}
return "redirect:/myAccount";
}
Front end:
<h1 style="color:green">Welcome <b>[[${#request.userPrincipal.principal.fullName}]]</b></h1>
<h2 style="color:green">My Details</h2>
<div th:if="${message}" class ="alert alert-success text-center">
[[${message}]]
</div>
<form th:action="#{/myAccount/update}" th:object="${user}"
method="post" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="email" th:field="*{email}" class="form-control" readonly="readonly" />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" placeholder="Leave blank if you don't want to change!" class="form-control"
minlength="6" maxlength="10"/>
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">First Name: </label>
<div class="col-8">
<input type="text" th:field="*{firstName}" class="form-control"
required minlength="2" maxlength="20"/>
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Last Name: </label>
<div class="col-8">
<input type="text" th:field="*{lastName}" class="form-control"
required minlength="2" maxlength="20" />
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Update Details</button>
</div>
</div>
</form>
Printed statements in console:
Updating user details...
Fetching user with id: 1
User fetched: com.example.Model.User#330603d0
Saving updated user: com.example.Model.User#330603d0
User saved: com.example.Model.User#330603d0
You are reassigning the user object received from the request to some other value. Check the below lines from the controller method
user = repo.findByEmail(principal.getName()); // this line reassigning the user object from the request to that of the one in database.
User updatedUser = service.updateAccount(user);
Because of this, user details are getting updated but with the existing data.
i have 2 entity class, parkingUser and parkingDetails, user can have many parking details(one to many).
parkingUserobject contain: [id, firstName, lastName,parkingDetails[id, entryDate,...,user_id(FK)]]
in parkingUser i had List of parkingDetails(its my FK)
#OneToMany(mappedBy = "parkingUsers", cascade = CascadeType.ALL, orphanRemoval = true)
private List<parkingDetails> parkingDetails = new ArrayList<parkingDetails>();
so now i have a thymeleaf page that recive from controller model of user and model of parking, its showing exist data on form input fields, and adding more data of exit date and exit time on the object in the c'tor on the controller that recive the form.
after that what i want to achive is to get the id of parking.id,
but its send me from a form the id of user.id
my controller with prints to understood what i get from the form:
(lets say i enter to form on user id =1, its create on the controoler new object of parkingDetails, and save it on db,
after that i want to delete the new object that created with id 35, and the object that pass from the form of parking.id, but i cant access this data.
#PostMapping("/saveDateAndTimeOfExitParking")
public String saveNewUserFromFormAction(
#ModelAttribute("user") parkingUsers parkingUsers,
#ModelAttribute("details") parkingDetails parkingDetails) {
//need to store to details table date and time
String str = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(parkingUsers);
//parkingUsers [id=1, firstName=David, lastName=Adams, license=2356584, parkingDetails=[]]
System.out.println(parkingDetails);
//parkingDetails [id=1, entryDate=2022-05-18, entryTime=08:35:20, exitDate=null, exitTime=null, parkingUsers=null]
System.out.println(parkingUsers.getParkingDetails());
//[]
parkingDetails d = new parkingDetails(
parkingDetails.getEntryDate(),
parkingDetails.getEntryTime(),
LocalDate.now(),
str);
d.setParkingUsers(parkingUsers);
parkingDetailsService.saveParkingUser(d);
//store the object of parking details on new db of history
//and then remove it from main schema
System.out.println(d.getId());
//35
System.out.println(parkingDetails.getId());
//1
parkingDetailsService.deleteParkingEntrySession(d.getId());
//i want to delete also the parkingDetails object from the form, but its gave me the id of parkingUser
//parkingDetailsService.deleteParkingEntrySession(parkingDetails.getId());
return "redirect:/exit";
}
my form:
<div class="formCenterAndWidth">
<form action="#" th:action="#{/saveDateAndTimeOfExitParking}" method="POST">
<input type="hidden" th:field="*{user.id}"/>
<div class="form-group row">
<label class="col-sm-2 col-form-label">User Id:</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:field="*{user.id}">
</div>
<label class="col-sm-2 col-form-label">Parking Id:</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:field="*{parking.id}">
</div>
<label class="col-sm-2 col-form-label">First Name:</label>
<div class="col-sm-10">
<input class="form-control" type="text" th:field="*{user.firstName}">
</div>
<label class="col-sm-2 col-form-label">Last Name:</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:field="*{user.lastName}">
</div>
<label class="col-sm-2 col-form-label" style="white-space: nowrap;">License Number</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:field="*{user.license}">
</div>
<label class="col-sm-2 col-form-label">Entry Date</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:field="*{parking.entryDate}">
</div>
<label class="col-sm-2 col-form-label">Entry Time</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:field="*{parking.entryTime}">
</div>
<label class="col-sm-2 col-form-label">Exit Date</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:value="${#dates.format(exitDateAndTime, 'dd-MM-yyyy')}">
</div>
<label class="col-sm-2 col-form-label">Exit Time</label>
<div class="col-sm-10">
<input readonly class="form-control" type="text" th:value="${#dates.format(exitDateAndTime, 'HH:mm:ss')}">
</div>
<br><br>
<button type="submit" class="btn btn-info col-7 center">Exit Car</button>
</div>
</form>
I'm trying to upload multiple files to springboot server from angular but I don't know why I'm getting error.But in postman the code is working fine and I'm able to upload files and output format.
I'm getting this error in browser
HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: "OK", url: 'http://localhost:8080/file/Upload', ok: false, …}
Error in backend terminal:
DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'fileN' is not present]
springboot restapi:
#postMapping(value="/Upload")
public ResponseEntity<List<String>> uploadFiles(#RequestParam ("fileN")List<MultipartFile> multipartFiles,#RequestParam("fileFormat")String id) throws IOException, FOPException, TransformerException{
List<String> filenames = new ArrayList<>();
try{
for(MultipartFile file : multipartFiles){
String filename = StringUtils.cleanPath(file.getOriginalFilename());
Path fileStorage = get(DIRECTORY, filename).toAbsolutePath().normalize();
copy(file.getInputStream(), fileStorage, REPLACE_EXISTING);
filenames.add(filename);
}
fileConversion.Convert(id);
}
catch (Exception e){
e.printStackTrace();
}
return ResponseEntity.ok().body(filenames);
user service:
constructor(private http: HttpClient){}
uploadFile(formData: FormData): Observable<any>{
const headerDist = {
'Access-Control-Allow-Origin': '*';
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept',
'access-control-allow-credentials':'true'
'Content-Type': 'multipart/form data; boundary=Inflow'
}
const requestOptions = {
headers: new HttpHeaders(headerDist),
};
return this.http.post('http://localhost:8080/file/Upload',formData,requestOptions);
}
appcomponent:
onSubmit(){
const formData = new formData();
formData.append('fileN', this.angForm.get('file1').value);
formData.append('fileN', this.angForm.get('file2').value);
formData.append('fileN', this.angForm.get('file3').value);
formData.append('fileN', this.angForm.get('fileFormat').value);
this.userService.uploadFile(formData).subscribe (
(res) => console.log(res),
(err) => console.log(err)
)};
html:
<h2 id="zone" xmlns="http://www.w3.org/1999/html">File Conversions</h2>
<form [formGroup]="angForm" enctype="multipart/form-data" (ngSubmit)="onSubmit()" >
<div class="main">
<div class="classOne">
<div class="form-two">
<input type="file" class="file-input" formControlName="file1" id="ixml" value="" accept="xml/*" required (change)='uploadF($any($event.target).files)' #open>
<div class="file-upload">
<p class="para-tag">Input XML :</p>
<button mat-raised-button color="primary" class="upload-btn" (click)="open.click()">Choose File</button>
</div>
</div>
</div>
<div *ngIf="angForm.controls['file1'].invalid" class ="alert alert-danger">
<div *ngIf="angForm.controls['file1'].errors.required"></div>
</div>
<div class="classTwo">
<div class="form-two">
<input type="file" class="file-input" formControlName="file2" id="udtd" value="" accept="dtd/*" required (change)="uploadFile($event.target.files)"#ele>
<div class="file-upload">
<p class="para-tag">Upload DTD : </p>
<button mat-raised-button color="primary" class="upload-btn" (click)="ele.click()">Choose File</button>
</div>
</div>
</div>
<div *ngIf="angForm.controls['file2'].invalid" class ="alert alert-danger">
<div *ngIf="angForm.controls['file2'].errors.required"></div>
</div>
<div class="classThree">
<div class="form-two">
<input type="file" class="file-input" formControlName="file3" id="uxsl" value="" accept="xsl/*" required (change)="uploading($event.target.files);"#choose>
<div class="file-upload">
<p class="para-tag">Upload XSL : </p>
<button mat-raised-button color="primary" class="upload-btn" (click)="choose.click()">Choose File</button>
</div>
</div>
</div>
<div *ngIf="angForm.controls['file3'].invalid" class ="alert alert-danger">
<div *ngIf="angForm.controls['file3'].errors.required"></div>
</div>
<div class="classFour">
<p class="para-tag-two">Choose Your Output Format :</p>
<div>
<input type="radio" id="pdf" value="pdf" name="fileFormat" formControlName="fileFormat">
<label for="pdf">PDF</label><br>
<input type="radio" id="ps" value="ps" name="fileFormat" formControlName="fileFormat">
<label for="ps">PostScript</label><br>
<input type="radio" id="png" value="png" name="fileFormat" formControlName="fileFormat">
<label for="png">PNG</label><br>
<input type="radio" id="txt" value="txt" name="fileFormat" formControlName="fileFormat">
<label for="txt">Text</label><br>
<input type="radio" id="print" value="print" name="fileFormat" formControlName="fileFormat">
<label for="print">Print</label><br><br>
</div>
</div>
<div *ngIf="angForm.controls['fileFormat'].invalid" class ="alert alert-danger">
<div *ngIf="angForm.controls['fileFormat'].errors.required"></div>
</div>
<div class="submit">
<button style="width:100px;" type="submit" [disabled]="angForm.invalid" mat-raised-button color="warn"disabled="true" >Submit</button>
</div>
</div>
</form>
I have a side bar for my user profile page which has two items for 1) Updating the information and 2) showing the reviews that the user has already written.
The first item works perfectly (as it includes a form and has a submit button). But for the second one, I have no idea. The goal is that when I click on My Reviews, a method from the controller class is called, the reviews of the user are extracted from the database and the results are shown on the right side of the page.
As I don't have a submit button or a form for the second item, I don't know how I can implement it.
Here is my code:
<div class="module-inner">
<div class="side-bar">
<nav class="side-menu">
<div class="col-xs-3">
<ul class="nav nav-pills nav-stacked">
<li class="active"><a data-toggle="pill" href="#profile">Profile</li>
<li class="active"><a data-toggle="pill" href="#review">My
Reviews</a></li>
</ul>
</div>
</nav>
</div>
<div class="content-panel">
<div class="col-xs-9">
<div class="tab-content">
<div id="profile" class="tab-pane fade">
<form class="form-horizontal" th:action="#{/edit_profile}"> <fieldset class="fieldset">
<h3 class="fieldset-title">Personal Info</h3>
<div class="form-group">
<label class="col-md-2 col-sm-3 col-xs-12 control-label">User
Name</label>
<div class="col-md-10 col-sm-9 col-xs-12">
<input type="text" class="form-control"
th:disabled="${currentUser.email}"
th:value="${currentUser.email}">
</div>
</div>
<div class="form-group">
<label class="col-md-2 col-sm-3 col-xs-12 control-label">First
Name</label>
<div class="col-md-10 col-sm-9 col-xs-12">
<input name="firstname" type="text" class="form-control"
th:value="${currentUser.firstname}">
</div>
</div>
<div class="form-group">
<label class="col-md-2 col-sm-3 col-xs-12 control-label">Last
Name</label>
<div class="col-md-10 col-sm-9 col-xs-12">
<input name="lastname" type="text" class="form-control"
th:value="${currentUser.lastname}">
</div>
</div>
</fieldset>
<hr>
<div class="form-group">
<div
class="col-md-10 col-sm-9 col-xs-12 col-md-push-2 col-sm-push-3 col-xs-push-0">
<input class="btn btn-primary" type="submit"
value="Update Profile">
</div>
</div>
</form>
</div>
<div id="review" class="tab-pane fade">
<h3>Menu 2</h3>
<p>You have no reviews yet.</p>
</div>
</div>
</div>
</div>
</div>
Here is my controller:
#RequestMapping(value = "/findUserReviews", method = RequestMethod.GET)
public ModelAndView findUserReviews() {
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
..
modelAndView.addObject("reviews", reviewRepository.findUserRevies());
modelAndView.setViewName("profile");
return modelAndView;
}
I use the following technologies: Spring boot, Hibernate and Thymeleaf.
Any help would be appreciated.
Final update: The provided code in the accepted answer works, provided that I don't return a ModelAndView but a List<Review>.
With Ajax calls you can call the controller endpoints using javascript. One ajax call looks like this :
function getReviews() {
$.ajax({
type: "GET",
url: "/users/findUserReviews", //example
dataType: "JSON",
success: function (data) {
//do something with this JSON
fillReviews(data);
}
});
}
Now you can use this function as an on-click event for your button. And the fillReviews() is a function that gets the element with id="review" from the jsp page and create the list tree with the fetched data.
function fillReviews(data) {
var reviewDiv= document.getElementById('review');
var reviewList = document.createElement('ul');
for ( var i=0 ; i < data.length; i++)
{
var reviewListItem = createListItem(data[i]);
reviewList.appendChild(reviewListItem);
}
reviewDiv.appendChild(reviewList);
}
And createListItem(data[i]) could look like this:
function createListItem(data)
{
var listItem = document.createElement('li');
listItem.innerHTML = data["reviewName"]; // for example ..
return listItem;
}
And now all you have to do is to call getReviews() here :
<button onclick="getReviews()"/>
EDIT : The "data" from the ajax call is a JSON. So the "/users/findUserReviews" should return a List<Review> for example. And there is no need to change your original "/findUserReviews" endpoint. This was only an example, you can create a new endpoint in your controller which returns a list.
Spring-controller is getting the MultipartFile, but its still throwing
"DefaultHandlerExceptionResolver:186 - Handler execution resulted in exception: Required MultipartFile parameter 'txnFile' is not present"
This is causing AngularJS client call to fail with 400.
JAVA-Spring-Controller:
#RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST)
public #ResponseBody Response uploadMultipleFileHandler(
#RequestParam("fileType") String fileType,
#RequestParam("fileEtx") String fileEtx,
#RequestParam("country") String country,
#RequestParam("network") String network,
#RequestParam("version") String version,
#RequestParam("txnDate") String txnDateStr,
#RequestParam("txnFile") MultipartFile txnFile,
#RequestParam("exceptionFile") MultipartFile exceptionFile){
Response response= null;
try {
Here, txnFile and exceptionFile is not null. Code runs file as well. But it still throws "DefaultHandlerExceptionResolver:186 - Handler execution resulted in exception: Required MultipartFile parameter 'txnFile' is not present"
Java-Spring-Config:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1000000" />
</bean>
HTML-AngularJS-Form:
<form method="POST" action="http://localhost:8080/recon-validation-tool/ctrl/core/uploadMultipleFile" enctype="multipart/form-data">
<fieldset>
<legend>Select Txn file:</legend>
<div class="form-group">
<label for="">Txn-file:</label>
<input type="file" file-model="txnFile"/>
</div>
</fieldset>
<fieldset>
<legend>Select Exec file:</legend>
<div class="form-group">
<label for="">Exec file:</label>
<input type="file" file-model="exceptionFile"/>
</div>
</fieldset>
<fieldset>
<legend>Please enter carrier details:</legend>
<div class="form-group">
<label for="txnDate">Date:</label>
<input type="date" name="txnDate" ng-model="formData.txnDate" value="2017-05-30">
</div>
<div class="form-group">
<label for="fileType">Type:</label>
<input type="text" name="fileType" ng-model="formData.fileType">
</div>
<div class="form-group">
<label for="fileEtx">Ext:</label>
<input type="text" name="fileEtx" ng-model="formData.fileEtx">
</div>
<div class="form-group">
<label for="country">country:</label>
<input type="text" name="country" ng-model="formData.country">
</div>
<div class="form-group">
<label for="country">network:</label>
<input type="text" name="network" ng-model="formData.network">
</div>
<div class="form-group">
<label for="version">version:</label>
<input type="text" name="version" ng-model="formData.version">
</div>
</fieldset>
<br>
<button ng-click="uploadFile()">Validate</button>
</form>
AngularJS-Controller:
controllerM.controller('HomeController', function($scope, $http, $location, $rootScope){
$scope.formData= {};
$scope.formData.fileEtx= "";
$scope.formData.fileType= "";
$scope.formData.country= "";
$scope.formData.network= "";
$scope.formData.version= "";
$scope.formData.txnDate= "";
$scope.uploadFile = function(){
var data = new FormData();
data.append('fileEtx', $scope.formData.fileEtx);
data.append('fileType', $scope.formData.fileType);
data.append('country', $scope.formData.country);
data.append('network', $scope.formData.network);
data.append('version', $scope.formData.version);
data.append('txnDate', $scope.formData.txnDate);
data.append('txnFile', $scope.txnFile);
data.append('exceptionFile', $scope.exceptionFile);
var uploadUrl = "http://localhost:8080/recon-validation-tool/ctrl/core/uploadMultipleFile";
$http.post(uploadUrl, data, {
headers: {'Content-Type': undefined},
transformRequest: angular.identity
})
.success(function(resp){
console.log(resp);
})
.error(function(resp){
console.log(resp);
});
};
});