I am building a web app and I need to pass the user's id to my front-end(angular) so I can do some ajax calls.
My question is, how can I give(store) the user's id to my front end? For example, a user can create a group by specifying a group name, but I need to be able to pass the users Id aswell in my ajax request(My backend endpoint needs both an Id and a name).
Angular:
$scope.createGroup = function() {
$http.post(BASE_URL + "/group", $scope.groupRequest).
success(function (data, status, headers, config) {
console.log("Success");
}).error(function(data, status, headers, config) {
$log.info("Error: status =" + status + ", body =" + JSON.stringify(data));
});
}
Front-end controller
#RequestMapping(value = "/group", method = RequestMethod.POST)
public ResponseEntity createGroup(#RequestBody Map body) {
try {
return restTemplate.postForEntity(URI.create(BASE_URL + "/group"), body, Map.class);
} catch (HttpClientErrorException e) {
LOG.warn("Error when trying to fetch groups", e);
return ResponseEntity.badRequest().body(e.getResponseBodyAsString());
}
}
Java Back end controller
#RequestMapping(value = "/group", method = RequestMethod.POST)
public ResponseEntity createGroup(#RequestBody #Valid GroupRequest groupRequest, BindingResult validation) {
if (validation.hasErrors()) {
throw new ValidationException(validation.getFieldErrors());
}
Long groupId = groupService.createGroup(groupRequest);
URI groupLocationURI = URI.create("/group/" + groupId);
return ResponseEntity.created(groupLocationURI).build();
}
Thanks
Related
I have two different Spring applications (app-one, app-two), app-one should receive a response and then redirect to app-two with some parameters. So, I have the following REST controller in app-one:
#RestController
public class RedirectController
{
#GetMapping(value = "/redirect")
public ResponseEntity<Void> redirectEndpoint(HttpServletRequest request,
RedirectAttributes redirectAttributes)
{
// Do some business logic
// Set parameters
redirectAttributes.addAttribute("attribute", "Value 1");
redirectAttributes.addFlashAttribute("flashAttribute", "Value 2");
// Redirect to success URL
String redirectURL = "http://app-two/success";
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(redirectURL));
return ResponseEntity.status(HttpStatus.FOUND).headers(headers).build();
}
}
And the following REST controller in app-two:
#RestController
public class SuccessController
{
#GetMapping(value = "/success")
public ResponseEntity<Void> successEndpoint(HttpServletRequest request, Model model,
#RequestParam(value = "attribute", required = false) String attribute,
#ModelAttribute(value = "flashAttribute") String flashAttribute)
{
// Get parameters
System.out.println("attribute: " + attribute);
System.out.println("flashAttribute: " + flashAttribute);
String flashAttributeFromModelMap = (String) model.asMap().get("flashAttribute");
System.out.println("flashAttributeFromModelMap: " + flashAttributeFromModelMap);
Map<String, ?> flashMap = RequestContextUtils.getInputFlashMap(request);
if (flashMap != null)
{
String flashAttributeFromFlashMap = (String) flashMap.get("flashAttribute");
System.out.println("flashAttributeFromFlashMap: " + flashAttributeFromFlashMap);
}
// Do some business logic
return ResponseEntity.status(HttpStatus.OK).build();
}
}
I was able to redirect successfully by returning FOUND (302). But when adding attributes to RedirectAttributes (in this case attribute and flashAttribute), these attributes are not found after the redirection done (attribute gets null and flashAttribute gets empty).
I tried to get the attributes values by different ways (#RequestParam, #ModelAttribute, model.asMap().get(), and RequestContextUtils.getInputFlashMap(request).get()) but none of them gets the correct value.
What I need is to get the correct attributes' values in successEndpoint. Any suggestions on how to accomplish that?
Thanks in advance.
I am sending the same Post request from the Angular Application and the Chrome poster.
From the Poster I specify the following:
URL: http://localhost:8080/rentapp/policy
Header:
content-type application/json
Content: body
{
"title": "NoSmoking",
"description":"NoSmoking"
}
In the Spring Controller I have the following code:
#PostMapping
// #Secured("ROLE_ADMIN")
public ResponseEntity<?> create(#Valid AttributeDTO policy, BindingResult bindingResult) {
logger.debug("CreatePolicy=" + policy);
AttributeDTO is defined as:
#Data
public class AttributeDTO {
private String title;
private String description;
}
I see that after the Poster request AttributeDTO is populated.
In Angular I Have the following method
public addPolicyWithObservable(policy: Policy): Observable<Policy> {
const headers = new Headers({'Content-Type': 'application/json'});
const options = new RequestOptions({headers: headers});
console.log("addPolicyWithObservable policy=" + policy + " url=" + this.url + " http=" + this.http);
const ret: Observable<Policy> = this.http.post(this.url, policy, options)
.map(this.extractData)
.catch(this.handleErrorObservable);
console.log ("addPolicyWithObservable ret=" + ret);
return ret;
}
private extractData(res: Response) {
const body = res.json();
return body.data || {};
}
private handleErrorObservable(error: Response | any) {
console.error(error.message || error);
return Observable.throw(error.message || error);
}
In the Policy model:
export class Policy {
public id: number;
public title: string;
public description: string;
toString(): string {
return this.title + ': ' + this.description;
}
}
I see that after this request the properties of AttributeDTO (title and description ) are null on the Java side. That is despite the fact that the request comes to the Java side and on the Angular side the logging before sending is correct, i.e the title and description are properly populated:
addPolicyWithObservable policy=NoSmoking: NoSmoking url=/api/policy http=[object Object]
createpolicy.service.ts:20 addPolicyWithObservable ret=[object Object]
That may be because you have no function to convert incoming JSON to actual objects, or you're not telling Spring that you expect it to consume JSON.
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
// #Secured("ROLE_ADMIN")
public ResponseEntity<?> create(#Valid AttributeDTO policy, BindingResult bindingResult) {
logger.debug("CreatePolicy=" + policy);
I'm not too sure if you gonna need this dependancy, but just in case.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
public addPolicyWithObservable(policy: Policy): Observable<Policy> {
const headers = new Headers({'Content-Type': 'application/json'});
const options = new RequestOptions({headers: headers});
console.log("addPolicyWithObservable policy=" + policy + " url=" + this.url + " http=" + this.http);
return this.http.post(this.url, policy, options)
.map((res) => res.json() as Policy)
.catch((err) => Observable.throw(console.error(err)));
}
try using the #RequestBody annotation for the AttributeDTO.
public ResponseEntity<?> create(
#RequestBody AttributeDTO policy,
BindingResult bindingResult)
{
logger.debug("CreatePolicy=" + policy);
...
I'm writing my first spring application, and would like to get experience to make an optimal and attractive code on spring.
I've some restcontrollers which have the big part of similar code
#RequestMapping(path = "/1154",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CreateUserResp processRequest(#RequestBody #Valid CreateUserReq request, BindingResult bindingResult) {
CreateUserResp response = new CreateUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = UserService.doSomething();
}
return response;
}
#RequestMapping(path = "/1155",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ChangeUserResp processRequest(#RequestBody #Valid ChangeUserReq request, BindingResult bindingResult) {
ChangeUserResp response = new ChangeUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = ChangeService.doSomething();
}
return response;
}
#RequestMapping(path = "/1156",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public AddUserResp processRequest(#RequestBody #Valid AddUserReq request, BindingResult bindingResult) {
AddUserResp response = new AddUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = AddService.doSomething();
}
return response;
}
#RequestMapping(path = "/1157",
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ModifyUserResp processRequest(#RequestBody #Valid ModifyUserReq request, BindingResult bindingResult) {
ModifyUserResp response = new ModifyUserResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
// main service
request = ModifyService.doSomething();
}
return response;
}
etc ....
The only differences in (path, #RequestBody and Responce objects, and called service ). So, I'll have 10-12 controllers like this one. Is it the way to make this code more optimal and not to write this repeatable blocks of code 10 times (spring methods or may be using generic class or methods). It's just example, not real code. Thx
Especial thanks to people who are very busy to answer but have time to put minus.
I have something quite similar in my application.
For example this is how my editProfile method from the User controller looks like:
#PostMapping(value = EDIT_CONTACT_INFO)
public ResponseEntity<?> editContactInfo(
#Autowired HttpServletRequest httpServletRequest,
#RequestBody #Valid ContactInfoDTO.Req requestBody,
BindingResult bindingResult
)
{
if (bindingResult.hasErrors())
// 400 - BAD REQUEST
return ErrorsDTO.from(bindingResult).responseEntity();
String userName = ControllerUtils.getUserName(httpServletRequest);
User user = userService.findByUserName(userName);
ContactInfo contactInfo = modelMapper.map(requestBody, ContactInfo.class);
if (!userService.editContactInfo(user, contactInfo))
// 500 - INTERNAL SERVER ERROR
return ErrorsDTO.from(INTERNAL_SERVER_ERROR).responseEntity();
// 200 - OK
return ResponseEntity.ok(null);
}
The majority of my API looks quite similar to yours. I've just written my custom mechanism to report errors, and I use a ResponseEntity instance to return data.
Also I have a library to pass data from DTOs to my Model and back (it's called ModelMapper).
edit: it looks like this blogpost covers your question:
http://blog.codeleak.pl/2013/09/request-body-validation-in-spring-mvc-3.2.html
If you really want to get down and dirty, you could write an interceptor, with a pointcut on a new annotation ValidateBinding and an argument of BindingResult. It would probably look something like:
#Around("#annotation(ValidateBinding) && execution(* *(..)) && args(bindingResult)
public Object handleInvalidBindings(ProceedingJoinPoint p, BindingResult bindingResult) {
if (bindingResult.hasErrors()){
GenericResponse response = createTypedResponse(p);
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
return response;
}
return pjp.proceed();
}
private GenericResponse createTypedResponse(ProceedingJoinPoint p) {
MethodSignature signature = (MethodSignature) p.getSignature();
Method method = signature.getMethod();
Class responseClass = method.getReturnType();
if(!GenericResponse.class.isAssignableFrom(responseClass)) {
throw new IllegalArgumentException("Could not create proper response class - it should implement the GenericResponse interface");
return (GenericResponse) responseClass.newInstance();
}
But I'm not guaranteeing the expression or the code works. This is more a rough guess on how it could look.
To do this, you'd need an interface GenericResponse which is implemented by your responseclasses, and has a setResultCode and setErrMsg.
I am creating a web page that allows the user to add new addresses, delete, and update the address list. My "add new address" is working, however "delete address" is not working.
Please check below my interface design for better understanding, help would be appreciated.
This is my working code for "add new address", and also the non working "Delete address" code.
In Controller:
#RequestMapping(value = "/addressBook", method = RequestMethod.GET)
public Object AddressBook(HttpSession session) throws Exception {
try{
String memberId= (String) session.getAttribute("memberId");
String addressId = (String) session.getAttribute("addressId");
List<Address> addresses = memberService.detail(memberId).getAddresses();
return new ModelAndView("account/addressBook").addObject("addressList", addresses);
} catch (Exception e) {
return new ModelAndView("account/addressBook")
.addObject("error", "No address book found");
}
}
#RequestMapping(value = "/addNewAddress", method = RequestMethod.GET)
public Object AddNewAddress() {
return "account/addNewAddress";
}
#RequestMapping(value = "/addNewAddress", method = RequestMethod.POST)
public Object AddNewAddress(#ModelAttribute AddAddress addAddress, HttpSession session, RedirectAttributes redirectAttributes) {
try {
String memberId = (String )session.getAttribute("memberId");
AddressDetail address1 = memberService.add(addAddress, memberId);
session.setAttribute("addressId", address1.getId());
return "redirect:/addressBook";
} catch (Exception e) {
return new ModelAndView("member/addNewAddress")
.addObject("addressList", addAddress)
.addObject("error", e.getMessage());
}
}
#RequestMapping(value="/deleteAddress" , method =RequestMethod.DELETE )
public Object DeleteAddress(HttpSession session) throws Exception {
String addressId = (String) session.getAttribute("addressId");
String memberId = (String) session.getAttribute("memberId");
AddressDetail addressDetail = memberService.deleteAddress(memberId, addressId);
return new ModelAndView("account/addressBook")
.addObject("success", "Address Deleted")
;
// "redirect:/addressBook";
}`
HTML Page:
<div class="8u 12u(5)">
<div class="table-wrapper">
<h4>Address Book</h4>
{{#addressList}}
<address>
{{street1}}, {{street2}}<br>
{{district}},{{cityTown}}<br>
{{postCode}}, {{provinceState}},<br>
{{countryCode}}<br>
<br>
Edit   <a class="confirm" href="/deleteAddress">Delete</a>
<hr>
</address>
{{/addressList}}
</div>
<b>Add New Address</b>
</div>
Member Service:
public AddressDetail add(AddAddress addAddress, String memberId) throws Exception {
Address address = new Address.Builder()
.setStreet1(addAddress.getStreet1())
.setStreet2(addAddress.getStreet2())
.setCityTown(addAddress.getCityTown())
.setDistrict(addAddress.getDistrict())
.setProvinceState(addAddress.getProvinceState())
.setPostCode(addAddress.getPostCode())
.setCountryCode(addAddress.getCountryCode())
.build();
RestRequest request = RestRequest.newBuilder()
.url("/member/" + memberId + "/address")
.post(address);
Address response = restClient.execute(configuration.serviceMemberName(), request, Address.class).body();
AddressDetail addressDetail = new AddressDetail();
addressDetail.setId(response.getId());
addressDetail.setDistrict(response.getDistrict());
addressDetail.setStreet1(response.getStreet1());
addressDetail.setStreet2(response.getStreet2());
addressDetail.setCityTown(response.getCityTown());
addressDetail.setProvinceState(response.getProvinceState());
addressDetail.setPostCode(response.getPostCode());
return addressDetail;
}
public AddressDetail addressDetail(String memberId, String addressId) throws Exception {
RestRequest request = RestRequest.newBuilder()
.url("/member/" + memberId + "/address/" + addressId)
.get();
Address response = restClient.execute(configuration.serviceMemberName(), request, Address.class).body();
AddressDetail addressDetail = new AddressDetail();
addressDetail.setId(response.getId());
addressDetail.setStreet1(response.getStreet1());
addressDetail.setStreet2(response.getStreet2());
addressDetail.setDistrict(response.getDistrict());
addressDetail.setCityTown(response.getCityTown());
addressDetail.setCountryCode(response.getCountryCode());
addressDetail.setPostCode(response.getPostCode());
addressDetail.setProvinceState(response.getProvinceState());
return addressDetail;
}
public AddressDetail deleteAddress(String memberId, String addressId) throws Exception {
RestRequest request = RestRequest.newBuilder()
.url("/member/" + memberId + "/address/" + addressId)
.delete();
Address response = restClient.execute(configuration.serviceMemberName(), request, Address.class).body();
AddressDetail addressDetail = new AddressDetail();
addressDetail.setId("");
addressDetail.setStreet1("");
addressDetail.setStreet2("");
addressDetail.setDistrict("");
addressDetail.setCityTown("");
addressDetail.setCountryCode("");
addressDetail.setPostCode("");
addressDetail.setProvinceState("");
return addressDetail;
}
From what I can see, you don't use any AJAX call to delete your record. You just created an anchor pointing to some "/deleteAddress" URL expecting that the browser will infer which HTTP verb to use from the URL semantics. You are far too optimistic. What's going to happen is that you'll fire an HTTP GET to the above "/deleteAddress" URL. But at the same time you instruct your Container to register the "/deleteAddress" URL for HTTP DELETE verb and so... nothing happens. Probably you'll get a 404 error.
If you want to use DELETE you will have to write a little javascript code to attach the "delete button" click to an AJAX call.
Something similar to the following:
$('.btn-delete').click(function () {
var id = //get the id somehow
doAjaxDelete(id);
});
function doAjaxDelete(id){
$.ajax({
type: "DELETE",
url: "your/path/to/record/" + id,
success: function(response){
// we have the response, do something to notify success
error: function(e){
//do something to notify failure
});
}
this code will work for all the elements having a ".btn-delete" class, so you should add it to your button (or use a different selctor, it's up to you). And you should also find a way to include in your DELETE URL an id to properly identify your record. LAst but not least, I used JQuery, so you should include it in your HTML page.
An easier way to proceed is to switch your method annotation from #RequestMapping(value="/deleteAddress" , method =RequestMethod.DELETE ) to #RequestMapping(value="/deleteAddress" , method =RequestMethod.GET). Honestly, I prefer to use the GET verb to retrieve stuff instead of deleting it.
I have built an application with Spring-boot and AngularJS with the REST End Point application. I got a little stuck with #RequesMapping in Spring Controller I've made. The problem is, I have the example url:
"localhost:8080/foo/bar/api/cardGenerated/0102".
'01' is first parameter and '02' is second parameter. How can I mapped into #RequestMapping Spring controller to get a url above.
Here's my controller :
#RestController
#RequestMapping("/api")
public class CardGeneratedResource {
#RequestMapping(value = "/cardGenerated/{branchCode}{cardType}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<CardGenerated> get(#PathVariable("branchCode") String branchCode,
#PathVariable("cardType") String cardType,
HttpServletResponse response) {
log.debug("REST request to get CardGenerated : " + branchCode + " and " + cardType);
CardGenerated cardGenerated = cardGeneratedRepository.
findTopByBranchCodeAndCardTypeOrderByCardNumberDesc(branchCode, cardType);
if (cardGenerated == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(cardGenerated, HttpStatus.OK);
}
}
so this is my AngularJS $resource:
'use strict';
angular.module('itmApp')
.factory('CardGenerated', function ($resource) {
return $resource('api/cardGenerated/:branchCode:cardType', {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
}
});
});
I always got 'Failed to load resource: the server responded with a status of 404 (Not Found)'.
Here you are missing / .
You have two path variable here.so default url is
localhost:8080/foo/bar/api/cardGenerated/FIRST_PATH_VARIABLE/SECOND_PATH_VARIABLE
branchCode (First path variabel)
cardType (Second path variable)
#RequestMapping(value = "/cardGenerated/{branchCode}/{cardType}"
And in frontend side too the same mistake while registering factory definition.
api/cardGenerated/:branchCode/:cardType'
All method is like
#RestController
#RequestMapping("/api")
public class CardGeneratedResource {
#RequestMapping(value = "/cardGenerated/{branchCode}/{cardType}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<CardGenerated> get(#PathVariable("branchCode") String branchCode,
#PathVariable("cardType") String cardType,
HttpServletResponse response) {
log.debug("REST request to get CardGenerated : " + branchCode + " and " + cardType);
CardGenerated cardGenerated = cardGeneratedRepository.
findTopByBranchCodeAndCardTypeOrderByCardNumberDesc(branchCode, cardType);
if (cardGenerated == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(cardGenerated, HttpStatus.OK);
}
}
and angular factory is like
'use strict';
angular.module('itmApp')
.factory('CardGenerated', function ($resource) {
return $resource('api/cardGenerated/:branchCode/:cardType', {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
}
});
});
NOTE: First try with any rest client or postman and make sure backend api is working properly also angular side check parameters are being passed correctly.