The following code works
public #ResponseBody
Map<String, Object> test(#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime startDate,
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime endDate) {
return null;
}
with this request
https://localhost:8080/api/v1/test?startDate=2000-10-31T01:30:00.000-00:00&endDate=2000-10-31T01:30:00.000-00:00
But the following code throws exception
public #ResponseBody
Map<String, Object> test(#RequestBody #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime startDate,
#RequestBody #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime endDate) {
return null;
}
with this body
{
"endDate":"2000-10-31T01:30:00.000-00:00",
"startDate":"2000-10-31T01:30:00.000-00:00"
}
has this exception
[org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected token (START_OBJECT), expected one of [VALUE_STRING, VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT] for java.time.ZonedDateTime value; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected one of [VALUE_STRING, VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT] for java.time.ZonedDateTime value
You must use #ModelAttribute or make object or map for mapping body, because Reflection utils not have opportunity for reading function parameter names (you cant specify property name for mapping).
public class User {
private String name;
private String occupation;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#RestController
public class MyController {
#ResponseStatus(value = HttpStatus.OK)
#PostMapping(value="/myfoo")
public void process2(#ModelAttribute("email") String email) {
}
#ResponseStatus(value = HttpStatus.OK)
#PostMapping(value="/vals")
public void process(#RequestBody MultiValueMap<String, String> values) {
}
#ResponseStatus(value = HttpStatus.OK)
#PostMapping(value="/user", consumes = MediaType.APPLICATION_JSON_VALUE)
public void process2(#RequestBody User user) {
}
}
ATTENTION
always set name of property for #RequestParam, else you can take error if somebody add not only this parameter or changed function signature
Related
Hello I have a problem with my service says that "Internal Server Error Exception occured! Exception details: IdentificationResponse cannot be cast to org.springframework.http.ResponseEntity"" any help on this matter ?
my code blocks below;
my IdentificationService Impl;
Slf4j
#Service
#RequiredArgsConstructor
public class IdentificationServiceImpl implements IdentificationService {
private final RestTemplate restTemplate;
#Value("${platform.url}")
private String platform;
private final ServiceConfig serviceConfig;
#Override
public ResponseEntity<IdentificationResponse> createIdentification(Request request) {
IdentificationRequest identificationRequest = new IdentificationRequest(serviceConfig.getIssuerKey(), serviceConfig.getSecureKey());
identificationRequest.setOperation(ClientConstants.CREATE_IDENTIFICATION_ADD);
HttpEntity<IdentificationRequest> requestHttpEntity = new HttpEntity<>(IdentificationRequest);
ResponseEntity<IdentificationResponse> response = restTemplate.exchange(platform, HttpMethod.POST, requestHttpEntity, IdentificationResponse.class);
return HandleResponse.handleResponse(response);
}
my handle Response class below;
public static <T> T handleResponse(ResponseEntity response) {
HttpStatus code = response.getStatusCode();
if (code == HttpStatus.OK || code == HttpStatus.CREATED) {
return (T) response.getBody();
} else if (code == HttpStatus.NO_CONTENT) {
return null;
} else if (code == HttpStatus.BAD_REQUEST)
throw new BadRequestException("BadRequest Exception occured while requesting! Exception details: " + response.getBody());
else if (code == HttpStatus.UNAUTHORIZED)
throw new UnauthorizedException("Unauthorized Exception occured while requesting! Exception details: " + response.getBody());
else
throw new HttpClientException("Exception occured! Exception details: " + response.getBody(), code.value());
}
}
my IdentificationRequest class below;
#Data
public class IdentificationRequest {
#Setter(AccessLevel.NONE)
#NotNull
#JsonProperty("IssuerKey")
private String issuerKey;
#Setter(AccessLevel.NONE)
#NotNull
#JsonProperty("SecurityKey")
private String secureKey;
#NotNull
#JsonProperty("TransactionId")
private String transactionId;
#NotNull
#JsonProperty("TransactionDate")
#DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss.SSS")
private String transactionDate;
#NotNull
#JsonProperty("Msisdn")
#Pattern(regexp = "^905.{9}", message = "must be of 12 char/digit")
private Long msisdn;
private String operation;
#NotNull
#JsonProperty("Package")
private String Package;
#NotNull
#JsonProperty("STB")
private Boolean isStb;
#JsonProperty("STB_SerialNumber")
private String stbSerialNumber;
#JsonProperty("STB_MacAddress")
private String stbMacAddress;
public IdentificationRequest(String issuerKey, String secureKey) {
this.issuerKey = issuerKey;
this.secureKey = secureKey;
}
}
my Request class below;
#Data
public class Request {
#JsonProperty("OrderId")
private String orderId;
#JsonProperty("OrderItemId")
private String orderItemId;
#JsonProperty("ProcessInstanceId")
private String processId;
#JsonProperty("step_id")
private String stepId;
#JsonProperty("step_name")
private String stepName;
my Response class below;
#Data
public class IdentificationResponse {
#JsonProperty("IsSuccessful")
private Boolean isSuccessful;
#JsonProperty("Code")
private Integer code;
#JsonProperty("Message")
private String message;
#JsonProperty("Data")
private Object data;
}
Do I need a map to response to response Entity?
As per your code, when you call response.getBody() method you'll get IdentificationResponse and it is converting it to ResponseEntity which is throwing ClassCastException.
if (code == HttpStatus.OK || code == HttpStatus.CREATED)
return (T) response.getBody();
You can either return response directly instead of response.getBody() to resolve your issue or you can follow the below implementation to your method if you want your createIdentification method to return IdentificationResponse instance
return HandleResponse.handleResponse(response, IdentificationResponse.class);
public static <T> T handleResponse(ResponseEntity response, Class<T> type) {
HttpStatus code = response.getStatusCode();
if (code == HttpStatus.OK || code == HttpStatus.CREATED)
return (T) response.getBody();
}
You can find some more info related to generics here
Basically you are trying to cast an object to an incompatible one, the cast could be done if both objects implement the same interface or if the hierarchy of objects allows it, if this cast cannot be achieved, it is a good idea to create a instance of the response object based on the value of the object that you retrieved.
#Component
public class JsonData {
#JsonProperty("id")
private Integer id;
#JsonProperty("createdAt")
private Date cratedAt;
#JsonProperty("name")
private String name;
#JsonProperty("email")
private String email;
#JsonProperty("imageUrl")
private String url;
public JsonData() {
}
public JsonData(Integer id, Date cratedAt, String name, String email, String url) {
this.id = id;
this.cratedAt = cratedAt;
this.name = name;
this.email = email;
this.url = url;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getCratedAt() {
return cratedAt;
}
public void setCratedAt(Date cratedAt) {
this.cratedAt = cratedAt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Controller
#RestController
public class JsonDataController {
#RequestMapping(value = "/template/products")
public void getAllData() {
RestTemplate template = new RestTemplate();
String url = "https://5ef99e4bbc5f8f0016c66d42.mockapi.io/testing/data";
ResponseEntity < JsonData[] > response = template.exchange(url, JsonData[].class);
for (JsonData jsonData: response.getBody()) {
System.out.println(jsonData.getName());
System.out.println(jsonData.getEmail());
}
}
}
I am trying to print json data that is array using rest template but I am getting error in this line "ResponseEntity < JsonData[] > response = template.exchange(url, JsonData[].class);" my error is "cannot resolve method" Can anyone tell me correct way of doing this .I am new to spring I do not have proper understanding it would be helpful if some one can give their suggeestion in this code
RestTemplate does not have any method with signature exchange(String, Class<T>).
That is why you are getting "cannot resolve method" error for template.exchange(url, JsonData[].class);.
Here is an example of correct usage of one of the methods from RestTemplate.exchange API:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<JsonData[]> response = restTemplate.exchange(url, HttpMethod.GET, null, JsonData[].class);
RestTemplate also has another method - getForEntity that makes a GET call with the given URL and expected return type. (without need for passing null for not required fields)
RestTemplate template = new RestTemplate();
String url = "https://5ef99e4bbc5f8f0016c66d42.mockapi.io/testing/data";
ResponseEntity <JsonData[]> response = template.getForEntity(url, JsonData[].class);
I have the following converter:
#Component
public class CountryEnumConverter implements Converter<String, CountryEnum> {
#Override
public CountryEnum convert(String country) {
CountryEnum countryEnum = CountryEnum.getBySign(country);
if (countryEnum == null) {
throw new IllegalArgumentException(country + " - Country is not supported!");
}
return countryEnum;
}
}
Registered it is invoked when used for RequestParam
#GetMapping(value = RestApiEndpoints.RESULTS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto> getResults(
Principal principal,
#RequestParam CountryEnum country) {
....
}
But this converter is never invoked when used for field in the RequstBody:
#GetMapping(value = RestApiEndpoints.RESULTS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto> getResults(
Principal principal,
#RequestBody MyBody myBody) {
....
}
public class MyBody {
#NotNull
private CountryEnum country;
public MyBody() {
}
public CountryEnum getCountry() {
return country;
}
public void setCountry(CountryEnum country) {
this.country = country;
}
}
Your existing org.springframework.core.convert.converter.Converter instance will only work with data submitted as form encoded data. With #RequestBody you are sending JSON data which will be deserialized using using the Jackson library.
You can then create an instance of com.fasterxml.jackson.databind.util.StdConverter<IN, OUT>
public class StringToCountryTypeConverter extends StdConverter<String, CountryType> {
#Override
public CountryType convert(String value) {
//convert and return
}
}
and then apply this on the target property:
public class MyBody {
#NotNull
#JsonDeserialize(converter = StringToCountryTypeConverter.class)
private CountryEnum country;
}
Given the similarity of the 2 interfaces I would expect that you could create one class to handle both scenarios:
public class StringToCountryTypeConverter extends StdConverter<String, CountryType>
implements org.springframework.core.convert.converter.Converter<String, CountryType> {
#Override
public CountryType convert(String value) {
//convert and return
}
}
I found out that if I add the following code to my CountryEnum will do the trick.
#JsonCreator
public static CountryEnum fromString(String value) {
CountryEnumConverter converter = new CountryEnumConverter();
return converter.convert(value);
}
I am getting unsupported mediatype error.
My User Profile class looks like this
Class UserProfile{
private int age;
private String name,
private String currenecy;
}
And this is the method in controller
#RequestMapping(value = "/Create", method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserProfileResponse> createUserProfile(#RequestBody UserProfile userProfile)
{
UserProfileResponse userProfileResponse = new UserProfileResponse();
int id = createUserProfileData(userProfile)
userProfileResponse.setId(id);
return new ResponseEntity<UserProfileResponse>(userProfileResponse,HTTPStatus.OK);
}
I am trying to send the request through POSTMAN but getting
Error 415--Unsupported Media Type
My Request in POstman looks like this
Content-Type:application/json
Accept:application/json
Method is : POST
{
"age":28,
"name":"Sam",
"currency": "INR"
}
Suggest me what I am missing?
Don't forget to select "JSON" format, filled in arbitrary JSON string in the textarea.
Also use either Accept or Content-type at a time.
If that doesn't work then can you check like below by removing consumes and adding headers manually.
#RequestMapping(value = "/Create", method=RequestMethod.POST, headers = "Accept=application/json",produces=MediaType.APPLICATION_JSON_VALUE)
I could see the response coming back with your code. I am deliberately returning the same object just to test the connectivity. Following is my code:
#RequestMapping(value = "/create", method= RequestMethod.POST,consumes= MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserProfile> createUserProfile(#RequestBody UserProfile userProfile)
{
System.out.println("Got request");
return new ResponseEntity<>(userProfile, HttpStatus.OK);
}
Used getter and setter in UserProfile
public class UserProfile {
private int age;
private String name;
private String currenecy;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCurrenecy() {
return currenecy;
}
public void setCurrenecy(String currenecy) {
this.currenecy = currenecy;
}
}
Finally after after spending some time.. I figured out why it was not working.
In my java based spring configuration file I missed "#EnableWebMvc".
After adding it, my problem got resolved.
#Configuration
**#EnableWebMvc** // This annotation was missing.
#ComponentScan(basePackages="com.hemant.*")
public class TestConfiguration {}
#Controller
#EnableWebMvc
#Validated
public class ChildController extends ParentController<InterfaceController> implements InterfaceController{
#Override
#RequestMapping(value = "/map/{name}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public List<Friends> getAllFriendsByName(
#Valid
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable("name") String name,
#RequestParam(value="pageSize", required=false) String pageSize,
#RequestParam(value="pageNumber", required=false) String pageNumber,
HttpServletRequest request) throws BasicException {
//Some logic over here;
return results;
}
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
Hi, I am trying to do pretty basic validation for a spring request parameter but it just doesn't seem to call the Exception handler, could someone point me into the right direction
P.S. I keep getting NoHandlerFoundException
Spring doesn't support #PathVariable to be validated using #Valid. However, you can do custom validation in your handler method or if you insist on using #Valid then write a custom editor, convert your path variable value to an object, use JSR 303 bean validation and then use #Valid on that object. That might actually work.
Edit:
Here's a third approach. You can actually trick spring to treat your path variable as a model attribute and then validate it.
1. Write a custom validator for your path variable
2. Construct a #ModelAttribute for your path variable and then use #Validator (yes not #Valid as it doesn't let you specify a validator) on that model attribute.
#Component
public class NameValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
String name = (String) target;
if(!StringUtils.isValidName(name)) {
errors.reject("name.invalid.format");
}
}
}
#RequestMapping(value = "/path/{name}", method = RequestMethod.GET)
public List<Friend> getAllFriendsByName(#ModelAttribute("name") #Validated(NameValidator.class) String name) {
// your code
return friends;
}
#ModelAttribute("name")
private String nameAsModelAttribute(#PathVariable String name) {
return name;
}