I'm making simple REST service and the client for it. I try to make some kind of security, so I generate UUID just, when you go to /login site:
#RequestMapping("/login")
public uuid getUUID()
{
temp = new uuid();
return temp;
}
Then by the client side I get this UUID. Now I want to pass this UUID to my service "getPerson" which looks like this:
#RequestMapping("/{userId}/getperson")
public Person getPerson(#PathVariable("userId") int user, uuid uuid)
{
if(System.currentTimeMillis() - uuid.getDate().getTime() < 60000 &&
uuid.getHash().toString().equals(temp.toString()))
return personService.getPerson(user);
else
return null;
}
What I wanted to achieve is a simple validation of UUID by comparing its timestamps and Strings with the previously made temp object. And here is my problem - I don't know how to pass the object uuid from client.
My very sophisticated client looks like this:
RestTemplate restTemplate = new RestTemplate();
uuid myUUID = restTemplate.getForObject("http://localhost:8080/login", uuid.class);
HttpEntity<uuid> requestUUID = new HttpEntity<uuid>(myUUID);
//HttpEntity<Person> request = new HttpEntity<Person>(new Person("John", "Great", 2));
//restTemplate.postForObject("http://localhost:8080/addperson", request, Person.class);
Person person = restTemplate.postForObject("http://localhost:8080/2/getperson", requestUUID, Person.class);
I don't know if this type of validation is safe, but would be great if you could tell me how to pass my object.
I found simple solution below,
It is the concept which i shown below
import java.util.UUID;
public class Person {
private UUID uuid;
public Person() {
}
public Person(UUID uuid) {
super();
this.uuid = uuid;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
#Override
public String toString() {
return "Person [uuid=" + uuid + "]";
}
}
Controller IWebExample Interface:
import java.util.UUID;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
public interface IWebExample {
#RequestMapping(value = "/login", method = RequestMethod.GET)
#ResponseBody
UUID getUUID();
#RequestMapping(value = "/{userId}/getPerson", method = RequestMethod.POST)
#ResponseBody
Person getPerson(int userId, UUID uuid);
}
Controller Implementation
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import com.finvendor.serverwebapi.resources.example.IWebExample;
import com.finvendor.serverwebapi.resources.example.Person;
#Controller
public class WebExampleImpl implements IWebExample {
#Override
public UUID getUUID() {
return UUID.randomUUID();
}
#Override
public Person getPerson(#PathVariable("userId") int userId, #RequestBody UUID uuid) {
return new Person(uuid);
}
}
Client code:
import java.util.UUID;
import org.springframework.web.client.RestTemplate;
public class ClientMainApp {
public static void main(String[] args) {
RestTemplate template=new RestTemplate();
UUID uuid = template.getForObject("http://localhost:8080/mylogin",UUID.class);
System.out.println("UUID="+uuid);
//here i am passing uuid object (as per your requirement)
Person person = template.postForObject("http://localhost:8080/1/getPerson", uuid, Person.class);
System.out.println(person);
}
}
Step to run
-Create war file
-Deploy in tomcat and
-Run the client code
Output would be:
UUID=e1e89e96-8118-446c-900d-d123b1b566ea
Person [uuid=e1e89e96-8118-446c-900d-d123b1b566ea]
In output you can observe we pass object uuid in postForObject(...) rest template method in client code.
The bottom line of solution of your problem is you need to use #RequestBody for uuid input param
Hope you got the idea and solution!!
Related
Check out these two controller methods. They both use the same object. The first one, called backtestStrategy correctly returns a JSON formatted response. The second one, called getAllStrategies, returns an array of values not in JSON format. All the values are there but none of the keys.
#RestController
#RequestMapping("/api/v1")
public class StrategyController {
#Autowired
private StrategyService strategyService;
#GetMapping("/backtest")
public Response backtestStrategy(#RequestParam String strategyName, #RequestParam String symbol) {
Response response = strategyService.backtestStrategy(strategyName,symbol);
return response;
}
#GetMapping("/strategies")
public List<Response> getAllStrategies() {
List<Response> strategies = strategyService.getAllStrategies();
return strategies;
}
}
Suggestions?
EDIT:
The first one apparently works because I create the Response object, populate it, save it to a db, and return the created object. The second one is reading from the db. The values are in the db correctly.
Here is the order of operations: controller calls service implementation which is defined by an interface. Service implementation calls repository, which makes the db query. Here is the query:
#Query(value="select * from strategy", nativeQuery = true)
List<Response> getAllStrategies();
You can use the following example to get the desired response:
Pojo.java
import lombok.AllArgsConstructor;
import lombok.Getter;
#Getter
#AllArgsConstructor
public class Pojo {
private String name;
}
DemoRestController.java
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
#RestController
#RequestMapping("/api/v1")
public class DemoRestController {
#GetMapping("/single")
public ResponseEntity<Pojo> single() {
return ResponseEntity.ok(new Pojo("one"));
}
#GetMapping("/multiple")
public ResponseEntity<List<Pojo>> multiple() {
return ResponseEntity.ok(Arrays.asList(new Pojo("one"), new Pojo("two")));
}
}
Output - Single
{
"name": "one"
}
Output - Multiple
[
{
"name": "one"
},
{
"name": "two"
}
]
I have this piece of code:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
protected Date statusDate;
But for somehow it accepts date formats like -
"statusDate": "2017-13-27"
or
"statusDate": "201823-12-12"
Is it possible to validate the format within the request (not manually)?
#JsonFormat is used to set the output format when you're returning the statusDate as response.
It is better you create a DTO object that will accept String statusDate and then convert it to Date format in your controller.
To validate the date in String format, you can use #Pattern
public class StatusDateDto {
#NotNull(message="Status date is a required field")
#Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}", message="Invalid status date")
private String statusDate;
//Getter and setter
}
public ResponseEntity<?> postStatusDate(#Valid #RequestBody StatusDateDto dateDto, BindingResult result) {
if (result.hasFieldErrors()) {
String errors = result.getFieldErrors().stream()
.map(p -> p.getDefaultMessage()).collect(Collectors.joining("\n"));
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
// Convert the String to Date after validation
return ResponseEntity.ok().build();
}
Yes, you can. Let me show u the details.
First, pls create a base controller to filter all the requests like below:
package com.letv.address.controller;
import com.letv.address.utils.ConstantCode;
import com.letv.address.utils.ResponseWrapper;
import com.letv.xice.core.controller.GlobalController;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import java.util.ArrayList;
import java.util.List;
/**
* Created by shichaoyang on 2017/1/10.
*/
public class BaseController extends GlobalController {
public ResponseWrapper requestCheckAndPost(BindingResult result) {
if (result.hasErrors()) {
List<Object> errorList = new ArrayList<>();
StringBuilder sBuilder = new StringBuilder();
for (ObjectError error : result.getAllErrors()) {
String fieldName = ((FieldError) error).getField();
String fieldMessage = error.getDefaultMessage();
sBuilder.append(fieldName)
.append(" ")
.append(getMessage(fieldMessage))
.append(";");
errorList.add(fieldName);
}
return new ResponseWrapper(
ConstantCode.FAILING_WITH_ERROR_PARAM_CODE
, errorList.toArray()
, ""
, sBuilder.toString()
);
}
return null;
}
}
From above code, the BindingResult will check the #JsonFormat or other components header, like #NotBlank, #Pattern and so on. If they hit the rule, they will be caught by the BindingResult and we can get the error. Below is the DTO object I used, just show it to u so that you can get more details:
package com.letv.address.controller.dto;
import com.letv.address.utils.ConstantCode;
import org.hibernate.validator.constraints.NotBlank;
/**
* Created by shichaoyang on 2016/12/23.
*/
public class ChildrenAreaSelectRequest{
#NotBlank(message = ConstantCode.REQUEST_VALIDATE_NOT_EMPTY)
private String areaIds;
public String getAreaIds() {
return areaIds;
}
public void setAreaIds(String areaIds) {
this.areaIds = areaIds;
}
}
Then in our business logic controller, we need to extend the base controller and write the codes like below:
package com.letv.address.controller;
import com.letv.address.controller.dto.ChildrenAreaSelectRequest;
import com.letv.address.controller.dto.ParentAreaSelectReqeust;
import com.letv.address.domain.Area;
import com.letv.address.service.ChildAreaService;
import com.letv.address.utils.ConstantCode;
import com.letv.address.utils.ResponseWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
/**
* Created by shichaoyang on 2016/12/12.
*/
#RestController("areaController")
public class AreaController extends BaseController {
#Autowired
protected ChildAreaService childAreaService;
/**
* get area info by parent id
*
* #param areaReqeust
* #param result
*/
#ResponseBody
#RequestMapping(value = ConstantCode.CHILD_AREA_PATH, method = {RequestMethod.POST})
public ResponseWrapper childArea(#RequestBody #Valid ParentAreaSelectReqeust areaReqeust, BindingResult result) {
ResponseWrapper validationWrapper = requestCheckAndPost(result);
if (validationWrapper != null) {
return validationWrapper;
}
List<Area> areaList = childAreaService.selectByParentId(areaReqeust.getParentId());
if (areaList == null || areaList.size() == 0) {
return new ResponseWrapper(ConstantCode.SUCCESS_WITH_EMPTY_DATA_CODE, new ArrayList<>());
} else {
return new ResponseWrapper(ConstantCode.SUCCESS_WITH_FILL_DATA_CODE, areaList);
}
}
}
By using above method, you can validate the field easily within the request. It's a beautiful way to achive this.
Hope that helps.
EDIT:
Replace the images with real codes so that anyone needs to test it.
Well, you need to write a custom date serialisation/de-serialisation class and throw a custom exception if the date format you receive while intercepting the data is not what you'd expected.
This answer will point you in the right direction of how to do so.
Also you can have the validator in your code to validate the payload and leave Jackson to it's simple state.
I am writing Rest Application with Spring boot. To expose my service code in as REST services.
I am able to expose my services in GET method when I am writing Post method with below code of Controller and Pojo class I am getting 405: Method Not Allowed error.
Not able to understand why ?
I have refered this link. and others related too but i could not figure out what is the problem.
Below is my controller and Pojo with jackson Json annotated code.
When i am calling using Advanced REST client - Chrome Web Store - Google and using as attached image i am getting below error.
In the same class i have some GET method that is working fine.
Error :
URL : http://localhost:8085/DBService/application/saveApplicationAnswer
{
"timestamp": 1470313096237
"status": 405
"error": "Method Not Allowed"
"exception": "org.springframework.web.HttpRequestMethodNotSupportedException"
"message": "Request method 'POST' not supported"
"path": "/DBService/application/saveApplicationAnswer"
}
DBService is my Context Name
As I have set server.context-path=/DBService in application.properties
import java.io.Serializable;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.drd.hotel.db.service.IApplicationDBService;
import com.drd.hotel.db.service.dto.application.CustomerDTO;
import com.drd.hotel.db.service.dto.application.ApplicationAnswerDTO;
import com.drd.hotel.db.service.dto.application.ApplicationQuestionsDTO;
import com.drd.hotel.db.service.dto.application.ApplicationRecommendationDTO;
import com.drd.hotel.db.service.util.ServicesConstants;
#RestController
#RequestMapping("/application")
public class ApplicationDBController<T, I extends Serializable> {
#Autowired
private IApplicationDBService applicationDBService;
#RequestMapping(value = "/saveApplicationAnswer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public int saveApplicationAnswer(#ModelAttribute(ServicesConstants.SURVERY_ANSWER_FL) ApplicationAnswerDTO applicationAnswer) {
LOG.info("ApplicationDBController fn saveApplicationAnswer BookingId {} {} {}",applicationAnswer.getBookingId(), ServicesConstants.CUSTOMER_ID_FL, applicationAnswer.getCustomerId());
return applicationDBService.saveapplicationAnswer(applicationAnswer);
}
}
My JSON:
{"answerId":1,"applicationQuestionId":1,"recommendId":1,"bookingId":123001,"customerId":19501,"reasonForCancelation":"I dont konw ","feedbackText":"I dont know what is this too bad design","applicationDate":"2016-08-04","funnelPageName":"I dont know what is the use of this.","applicationReferenceSource":"I dont knwo what is this field for","languageId":1}
MY Pojo annotated with JSON :
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion;
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
#JsonSerialize(include =Inclusion.NON_NULL)
public class ApplicationAnswerDTO {
private int answerId;
private int applicationQuestionId;
private int recommendId;
private int bookingId;
private int customerId;
private String reasonForCancelation;
private String feedbackText;
private Date applicationDate;
private String funnelPageName;
private String applicationReferenceSource;
private int languageId;
public int getAnswerId() {
return answerId;
}
public void setAnswerId(int answerId) {
this.answerId = answerId;
}
public int getApplicationQuestionId() {
return applicationQuestionId;
}
public void setApplicationQuestionId(int applicationQuestionId) {
this.applicationQuestionId = applicationQuestionId;
}
public int getRecommendId() {
return recommendId;
}
public void setRecommendId(int recommendId) {
this.recommendId = recommendId;
}
public int getBookingId() {
return bookingId;
}
public void setBookingId(int bookingId) {
this.bookingId = bookingId;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getFeedbackText() {
return feedbackText;
}
public void setFeedbackText(String feedbackText) {
this.feedbackText = feedbackText;
}
public Date getApplicationDate() {
return applicationDate;
}
public void setApplicationDate(Date applicationDate) {
this.applicationDate = applicationDate;
}
public String getFunnelPageName() {
return funnelPageName;
}
public void setFunnelPageName(String funnelPageName) {
this.funnelPageName = funnelPageName;
}
public String getApplicationReferenceSource() {
return applicationReferenceSource;
}
public void setApplicationReferenceSource(String applicationReferenceSource) {
this.applicationReferenceSource = applicationReferenceSource;
}
public int getLanguageId() {
return languageId;
}
public void setLanguageId(int languageId) {
this.languageId = languageId;
}
public String getReasonForCancelation() {
return reasonForCancelation;
}
public void setReasonForCancelation(String reasonForCancelation) {
this.reasonForCancelation = reasonForCancelation;
}
#Override
public String toString() {
return "ApplicationAnswerDTO [answerId=" + answerId + ", applicationQuestionId="
+ applicationQuestionId + ", recommendId=" + recommendId
+ ", bookingId=" + bookingId + ", customerId=" + customerId
+ ", reasonForCancelation="
+ reasonForCancelation + ", feedbackText="
+ feedbackText + ", applicationDate=" + applicationDate
+ ", funnelPageName=" + funnelPageName
+ ", applicationReferenceSource=" + applicationReferenceSource
+ ", languageId=" + languageId + "]";
}
}
Thanks in advance for any kind of info and suggestion.
Can you check the method type which your are requesting.
In the screen shot which your shared it is displaying only get and head method are allowed.
I have tried your code in my Soap ui. It is displaying the below response.
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 04 Aug 2016 13:03:11 GMT
1999999999
It is displaying the response which you shared when i try to call the same service using Get method.Below is the response.
{
"timestamp": 1470315684018,
"status": 405,
"error": "Method Not Allowed",
"exception": "org.springframework.web.HttpRequestMethodNotSupportedException",
"message": "Request method 'GET' not supported",
"path": "/saveApplicationAnswer"
}
and code I used is
{
#RequestMapping(value = "/saveApplicationAnswer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public int saveApplicationAnswer(#ModelAttribute("hello") ApplicationAnswerDTO applicationAnswer) {
System.out.println(applicationAnswer);
return 1999999999;
}
Please try with different tools preferably soap ui.
#RequestMapping(value = "/saveApplicationAnswer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public int saveApplicationAnswer(#RequestBody(ServicesConstants.SURVERY_ANSWER_FL) ApplicationAnswerDTO applicationAnswer) {
LOG.info("ApplicationDBController fn saveApplicationAnswer BookingId {} {} {}",applicationAnswer.getBookingId(), ServicesConstants.CUSTOMER_ID_FL, applicationAnswer.getCustomerId());
return applicationDBService.saveapplicationAnswer(applicationAnswer);
}
I have changed my parameter annotation refering this post.
From
#ModelAttribute(ServicesConstants.SURVERY_ANSWER_FL) ApplicationAnswerDTO applicationAnswer
To
#RequestBody(ApplicationAnswerDTO applicationAnswer
It worked for me. seems #RequestBody is correct. But i dont know the different between #RequestBody and #ModelAttribute. If any one knows the different please share here. That will be helpful for some one.
When i am pitting #RequestBody(ApplicationAnswerDTO applicationAnswer) It worked for me.
Any way Thanks for every one for your help and suggestion.
My Spring Controller of Spring JSON application returns a JSONObject. On accessing the url, i am getting 406 error page.
It works when i return String or ArrayList.
Spring Controller:
package com.mkyong.common.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONException;
import org.json.JSONObject;
#Controller
public class JSONController {
#RequestMapping("/test")
#ResponseBody
public JSONObject test() {
try {
JSONObject result = new JSONObject();
result.put("name", "Dade")
.put("age", 23)
.put("married", false);
return result;
} catch (JSONException ex) {
Logger.getLogger(JSONController.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
How can i resolve this issue? Thanks for help. I am new to Spring MVC, couldn't found resolution to this issue in the existing SO answers.
You're trying to manually do something that Spring MVC already it automatically for you. Spring automatically deduces a representation of the returning type and does a converstion. How it does it you can learn from http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc. In your case its converting to JSON.
It works when i return String or ArrayList
What happens under the hood is that Spring MVC is using Jackson library, to convert the return type to JSON. And since it has no issue converting the String or List type, all works OK.
What happens in the code you've posted is that, Jackson's object mapper is trying to convert JSONObject instance to JSON, and this fails, cause jackson expects a POJO object which JSONObject instance isn't.
To have it work you should simply write your POJO and return it. So something like
public class Person {
private String name;
private Integer age;
private Boolean married;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getMarried() {
return married;
}
public void setMarried(Boolean married) {
this.married = married;
}
}
and have your method changed to
#RequestMapping("/test")
#ResponseBody
public Person test() {
Person person = new Person();
person.setName("Dade");
person.setAge(23);
person.setMarried(false);
return person;
}
For what concerns your error, the same exception you will see in the working example if you for example delete getters and setters, or name them wrongly, an exception happens while trying to convert to a representation and you get a 406 error
I think you need to set headers in #RequestMapping and return HashMap.
#RequestMapping(value = "json", method = RequestMethod.GET, headers = "Accept=application/json")
public #ResponseBody
Map<String, String> helloJson() {
HashMap<String, String> map = new HashMap<String, String>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
return map;
}
I'm using REST using Spring & Java 1.7
I've below model class:
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
And my controller is mapped to GET /person/info API. So, the controller fetches name & shows it as JSON response.
Here is the controller:
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#RequestMapping(value = "persons/info", method = RequestMethod.POST, consumes="application/json", produces="application/json")
public ResponseEntity<String> getPersonInfo(#RequestBody String body) throws Exception {
String personInfo = null;
MyServiceJson myServiceJson = MyServiceJsonFactory.getMyServiceObject(body);
personInfo = myService.getPersonInfo(myServiceJson);
HttpHeaders responseHeader = new HttpHeaders();
return Util.getResponse(personInfo, responseHeader, HttpStatus.OK);
}
I'm getting the JSON response as shown below:
{"name":"Jack"}
The problem is here is that this name string must be personName as shown below:
{"PersonName":"Jack"}
I believe it is taking the variable name from model & sending it as it is. Can any one tell me if it is possible to have different attribute name as "PersonName" with some annotation change in REST service??
I'll appreciate if someone can shed some light here!
Thanks!
If you are using the Jackson library, I guess you can spcify in that way:
#JsonProperty("PersonName")
public String getName() {
return name;
}