HTTP Status 400 - Required String parameter 'walletName' is not present - java

I work with Java/ Spring MVC RESTful app and get 400 HTTP status error while doing a POST request. The #RestController method is provided,
#RequestMapping(value = "/generateAddress", method = RequestMethod.POST)
public ResponseEntity<WalletInfoWrapper> generateAddress(#RequestParam("walletName") String walletName,
#RequestParam("currencyName") String currencyName) {
logger.info("walletName {} and currencyName {}", walletName, currencyName);
// return if the wallet name or the currency is null
if (Objects.isNull(walletName) || Objects.isNull(currencyName)) {
return new ResponseEntity<WalletInfoWrapper>(HttpStatus.NOT_ACCEPTABLE);
}
WalletInfo walletInfo = walletService.generateAddress(walletName, currencyName);
if (Objects.isNull(walletInfo)) {
return new ResponseEntity<WalletInfoWrapper>(HttpStatus.NOT_ACCEPTABLE);
}
WalletInfoWrapper walletInfoWrapper = new WalletInfoWrapper();
walletInfoWrapper.setName(walletInfo.getName());
return new ResponseEntity<WalletInfoWrapper>(walletInfoWrapper, HttpStatus.CREATED);
}
The POST request in the Postman provided below,
The error message informs, Required String parameter 'walletName' is not present
I can also provide the code for the services and the dataase layers for observing the drop-down operations. What is the issue here?
UPDATE
I updated the Postman request like this and still having the same error,
UPDATE 1
I still have the same issue,
I POST with the data,
{"walletName":"puut","currencyName":"Bitcoin"}
The code is provided below,
#RequestMapping(value = "/generateAddress", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<WalletInfoWrapper> generateAddress(#RequestBody WalletWithMoneyRequest walletWithMoneyRequest) {
String walletName = walletWithMoneyRequest.getWalletName();
String currencyName = walletWithMoneyRequest.getCurrencyName();
logger.info("walletName {} and currencyName {}", walletName, currencyName);
// return if the wallet name or the currency is null
if (Objects.isNull(walletName) || Objects.isNull(currencyName)) {
return new ResponseEntity<WalletInfoWrapper>(HttpStatus.NOT_ACCEPTABLE);
}
WalletInfo walletInfo = walletService.generateAddress(walletName, currencyName);
if (Objects.isNull(walletInfo)) {
return new ResponseEntity<WalletInfoWrapper>(HttpStatus.NOT_ACCEPTABLE);
}
WalletInfoWrapper walletInfoWrapper = new WalletInfoWrapper();
walletInfoWrapper.setName(walletInfo.getName());
return new ResponseEntity<WalletInfoWrapper>(walletInfoWrapper, HttpStatus.CREATED);
}
The POJO is provided,
private class WalletWithMoneyRequest {
String walletName;
String currencyName;
public WalletWithMoneyRequest(String walletName, String currencyName) {
this.walletName = walletName;
this.currencyName = currencyName;
}
public WalletWithMoneyRequest() {
}
public String getWalletName() {
return walletName;
}
public String getCurrencyName() {
return currencyName;
}
public void setCurrencyName(String currencyName) {
this.currencyName = currencyName;
}
public void setWalletName(String walletName) {
this.walletName = walletName;
}
}
This is the error message,
Here ia the Tomcat server info,
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver: 08/19/2017 19:45:55 - Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `mobi.puut.controllers.WalletRestController$WalletWithMoneyRequest` (although at least one Creator exists): can only instantiate non-static inner class by using default, no-argument constructor; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `mobi.puut.controllers.WalletRestController$WalletWithMoneyRequest` (although at least one Creator exists): can only instantiate non-static inner class by using default, no-argument constructor
at [Source: (PushbackInputStream); line: 1, column: 2]
Tomcat Localhost log
Tomcat Catalina log

Edit
In your Postman request, instead of sending JSON, send the values as x-www-form-urlencoded.

Your controller is expecting 2 request parameters that normally look like this: /someurl?walletName=my-wallets-name&currencyName=dollars.
You're sending a json string in the post body, but no formal parameters. You need to update either your POST, or your controller to make the two ends agree. I think you probably want to replace the two #RequestParam annotated Strings, with a Java pojo that has two String members: walletName and currencyName, drop that pojo in your request method as an argument and precede it with the annotation #RequestBody. This will match your json post.
To have your controller accept the post with JSON in the body edit it like this:
#RequestMapping(value = "/generateAddress", method = RequestMethod.POST)
public ResponseEntity<WalletInfoWrapper> generateAddress(#RequestBody
WalletWithMoneyRequest myJsonRequestComingIn) {
logger.info("walletName {} and currencyName {}", myJsonRequestComingIn.getWalletName(), myJsonRequestComingIn.getCurrencyName());
And your pojo
public class WalletWithMoneyRequest{
private String walletName;
private String currencyName;
//getters and setters down here.

To elaborate on zerpsed's answer.
Change the signature to:
public ResponseEntity<WalletInfoWrapper> generateAddress(#ResponseBody WalletPOJO walletCurrency)
Where the WalletPOJO has the two fields walletName and currencyName

I believe the issue is solved for now. I have used a POJO as suggested with the #RequestBody parameter in the RESTful method. The catch here is I need to make the POJO out of the class (in the same file though) and later, put in the entity directory as an entity.
class WalletWithMoneyRequest {
String walletName;
String currencyName;
public WalletWithMoneyRequest(String walletName, String currencyName) {
this.walletName = walletName;
this.currencyName = currencyName;
}
public WalletWithMoneyRequest() {
}
public String getWalletName() {
return walletName;
}
public String getCurrencyName() {
return currencyName;
}
public void setCurrencyName(String currencyName) {
this.currencyName = currencyName;
}
public void setWalletName(String walletName) {
this.walletName = walletName;
}
}
The main issue is believe was an error in the HQL,
I wrote currency =: currency where it should be currency = :currency
I still can't have the data in the database as I will need to modify the method in the database layer.

En POSTMAN set variables in params

Related

Custom validations for GET mapping with pathvariable

I want to do a custom validations using spring boot for below method.I want to validate "id" if it is other than a to z, 0 to 9 and - then I want to set error message and pass that in ResponseEntity.
#RestController
public class DataController {
#Autowired DataService dataService;
#RequestMapping("/data/{id}")
public ResponseEntity<Data> getData(#PathVariable String id)
{
Data messages = dataService.getData(id, Data.DataFormat.ALL);
return new ResponseEntity<>(messages, HttpStatus.OK);
}
You can't validate a single primitive type in parameter automatically.
You have to validate it manually.
Try this:
private static final Pattern ACCEPTED_CHARACTERS = Pattern.compile("^[a-z0-9\\-]+$");
#RequestMapping("/data/{id}")
public ResponseEntity<Data> getData(#PathVariable String id)
{
if (!ACCEPTED_CHARACTERS.matcher(id).matches()) {
return new ResponseEntity<>("Your messge", YOUR CODE);
}
Data messages = dataService.getData(id, Data.DataFormat.ALL);
return new ResponseEntity<>(messages, HttpStatus.OK);
}
In real app, the test for pattern should be done in an utility class
Another solution: You can use #Pattern and don't forget to use #Validated before your class.
#RestController
#RequestMapping("required url")
#Validated
public class yourClassName{
#RequestMapping("/data/{id}")
public ResponseEntity<Data> getData(#Valid #Pattern(regexp = "^[a-z0-9\\-]+$",message = "Your custom message") #PathVariable String id){
Data messages = dataService.getData(id, Data.DataFormat.ALL);
return new ResponseEntity<>(messages, HttpStatus.OK);
}
}

MissingServletRequestParameterException with PostMapping in Spring MVC

I'm getting this error when I try to create an instance of an Object and store it into a database:
org.springframework.web.bind.MissingServletRequestParameterException
The code for the method:
#PostMapping(path="accounts/add", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public
#ResponseBody
String addAccount(#RequestParam String accountNumber, #RequestParam String accountName ) {
Account account = new Account();
account.setAccountName(accountName);
account.setAccountNumber(accountNumber);
accountRepository.save(account);
return "Saved";
}
When I use PostMan and try to enter the JSON in, it gives me this message. What am I doing wrong?
Since your POSTing (or PUTing?) JSON as the content body, you cannot use #RequestParam to deconstruct it. That is used for query or form parameters. You need to specify a single method parameter with #RequestBody, which is a class that looks something like the following.
public class Account {
public String accountNumber;
public String accountName;
// setters and getters removed for brevity
}
See this answer for more info: #RequestBody and #ResponseBody annotations in Spring

Parsing JSON with RequestBody in Spring

So I've been having some issues with #RequestBody in Spring. I've had no problems at all when it comes to returning objects and having Spring automatically parse them. I have no issue if I only declare a String as an argument for the method. Here is the code, I'll begin with what I want to receive:
public class ToParse {
private String name;
ToParse() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here is the controller:
#RestController
#RequestMapping("/test")
public class ConcreteTestController implements TestController {
#RequestMapping(method = RequestMethod.POST, consumes = {"application/json"})
#ResponseStatus(value = HttpStatus.OK)
#Override
public ToParse getSilly(#RequestBody ToParse toParse) {
ToParse toReturn = new ToParse();
toReturn.setName("stuff");
return toReturn;
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public ToParse getSilly(String test) {
ToParse toReturn = new ToParse();
toReturn.setName(test);
return toReturn;
}
}
Note that the second method works just fine. The header I am sending in is "application/json" for the first method and the JSON is:
{
“name”:”blablabla”
}
I get a 415 error when trying to call the first method using the rest client in intelliJ. My configuration is in XML and it is .
What am I doing wrong? There are many answers to these types of question but usually they are resolved by fixing header or Springconfig. I cannot see what I have done wrong.
You are getting a HTTP Error 415 Unsupported media type because you are sending a POST request without adding the Content-Type: application/json header.
The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17

Spring MVC wrap a lot of #RequestParam to an Object [duplicate]

Suppose i have a page that lists the objects on a table and i need to put a form to filter the table. The filter is sent as an Ajax GET to an URL like that: http://foo.com/system/controller/action?page=1&prop1=x&prop2=y&prop3=z
And instead of having lots of parameters on my Controller like:
#RequestMapping(value = "/action")
public #ResponseBody List<MyObject> myAction(
#RequestParam(value = "page", required = false) int page,
#RequestParam(value = "prop1", required = false) String prop1,
#RequestParam(value = "prop2", required = false) String prop2,
#RequestParam(value = "prop3", required = false) String prop3) { ... }
And supposing i have MyObject as:
public class MyObject {
private String prop1;
private String prop2;
private String prop3;
//Getters and setters
...
}
I wanna do something like:
#RequestMapping(value = "/action")
public #ResponseBody List<MyObject> myAction(
#RequestParam(value = "page", required = false) int page,
#RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }
Is it possible?
How can i do that?
You can absolutely do that, just remove the #RequestParam annotation, Spring will cleanly bind your request parameters to your class instance:
public #ResponseBody List<MyObject> myAction(
#RequestParam(value = "page", required = false) int page,
MyObject myObject)
I will add some short example from me.
The DTO class:
public class SearchDTO {
private Long id[];
public Long[] getId() {
return id;
}
public void setId(Long[] id) {
this.id = id;
}
// reflection toString from apache commons
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Request mapping inside controller class:
#RequestMapping(value="/handle", method=RequestMethod.GET)
#ResponseBody
public String handleRequest(SearchDTO search) {
LOG.info("criteria: {}", search);
return "OK";
}
Query:
http://localhost:8080/app/handle?id=353,234
Result:
[http-apr-8080-exec-7] INFO c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]
I hope it helps :)
UPDATE / KOTLIN
Because currently I'm working a lot of with Kotlin if someone wants to define similar DTO the class in Kotlin should have the following form:
class SearchDTO {
var id: Array<Long>? = arrayOf()
override fun toString(): String {
// to string implementation
}
}
With the data class like this one:
data class SearchDTO(var id: Array<Long> = arrayOf())
the Spring (tested in Boot) returns the following error for request mentioned in answer:
"Failed to convert value of type 'java.lang.String[]' to required type
'java.lang.Long[]'; nested exception is
java.lang.NumberFormatException: For input string: \"353,234\""
The data class will work only for the following request params form:
http://localhost:8080/handle?id=353&id=234
Be aware of this!
Since the question on how to set fields mandatory pops up under each post, I wrote a small example on how to set fields as required:
public class ExampleDTO {
#NotNull
private String mandatoryParam;
private String optionalParam;
#DateTimeFormat(iso = ISO.DATE) //accept Dates only in YYYY-MM-DD
#NotNull
private LocalDate testDate;
public String getMandatoryParam() {
return mandatoryParam;
}
public void setMandatoryParam(String mandatoryParam) {
this.mandatoryParam = mandatoryParam;
}
public String getOptionalParam() {
return optionalParam;
}
public void setOptionalParam(String optionalParam) {
this.optionalParam = optionalParam;
}
public LocalDate getTestDate() {
return testDate;
}
public void setTestDate(LocalDate testDate) {
this.testDate = testDate;
}
}
//Add this to your rest controller class
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String testComplexObject (#Valid ExampleDTO e){
System.out.println(e.getMandatoryParam() + " " + e.getTestDate());
return "Does this work?";
}
I have a very similar problem. Actually the problem is deeper as I thought. I am using jquery $.post which uses Content-Type:application/x-www-form-urlencoded; charset=UTF-8 as default. Unfortunately I based my system on that and when I needed a complex object as a #RequestParam I couldn't just make it happen.
In my case I am trying to send user preferences with something like;
$.post("/updatePreferences",
{id: 'pr', preferences: p},
function (response) {
...
On client side the actual raw data sent to the server is;
...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...
parsed as;
id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en
and the server side is;
#RequestMapping(value = "/updatePreferences")
public
#ResponseBody
Object updatePreferences(#RequestParam("id") String id, #RequestParam("preferences") UserPreferences preferences) {
...
return someService.call(preferences);
...
}
I tried #ModelAttribute, added setter/getters, constructors with all possibilities to UserPreferences but no chance as it recognized the sent data as 5 parameters but in fact the mapped method has only 2 parameters. I also tried Biju's solution however what happens is that, spring creates an UserPreferences object with default constructor and doesn't fill in the data.
I solved the problem by sending JSon string of the preferences from the client side and handle it as if it is a String on the server side;
client:
$.post("/updatePreferences",
{id: 'pr', preferences: JSON.stringify(p)},
function (response) {
...
server:
#RequestMapping(value = "/updatePreferences")
public
#ResponseBody
Object updatePreferences(#RequestParam("id") String id, #RequestParam("preferences") String preferencesJSon) {
String ret = null;
ObjectMapper mapper = new ObjectMapper();
try {
UserPreferences userPreferences = mapper.readValue(preferencesJSon, UserPreferences.class);
return someService.call(userPreferences);
} catch (IOException e) {
e.printStackTrace();
}
}
to brief, I did the conversion manually inside the REST method. In my opinion the reason why spring doesn't recognize the sent data is the content-type.
While answers that refer to #ModelAttribute, #RequestParam, #PathParam and the likes are valid, there is a small gotcha I ran into. The resulting method parameter is a proxy that Spring wraps around your DTO. So, if you attempt to use it in a context that requires your own custom type, you may get some unexpected results.
The following will not work:
#GetMapping(produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CustomDto> request(#ModelAttribute CustomDto dto) {
return ResponseEntity.ok(dto);
}
In my case, attempting to use it in Jackson binding resulted in a com.fasterxml.jackson.databind.exc.InvalidDefinitionException.
You will need to create a new object from the dto.
Yes, You can do it in a simple way. See below code of lines.
URL - http://localhost:8080/get/request/multiple/param/by/map?name='abc' & id='123'
#GetMapping(path = "/get/request/header/by/map")
public ResponseEntity<String> getRequestParamInMap(#RequestParam Map<String,String> map){
// Do your business here
return new ResponseEntity<String>(map.toString(),HttpStatus.OK);
}
Accepted answer works like a charm but if the object has a list of objects it won't work as expected so here is my solution after some digging.
Following this thread advice, here is how I've done.
Frontend: stringify your object than encode it in base64 for submission.
Backend: decode base64 string then convert the string json into desired object.
It isn't the best for debugging your API with postman but it is working as expected for me.
Original object: { page: 1, size: 5, filters: [{ field: "id", value: 1, comparison: "EQ" }
Encoded object: eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50IiwiY29tcGFyaXNvbiI6Ik5VTEwifV19
#GetMapping
fun list(#RequestParam search: String?): ResponseEntity<ListDTO> {
val filter: SearchFilterDTO = decodeSearchFieldDTO(search)
...
}
private fun decodeSearchFieldDTO(search: String?): SearchFilterDTO {
if (search.isNullOrEmpty()) return SearchFilterDTO()
return Gson().fromJson(String(Base64.getDecoder().decode(search)), SearchFilterDTO::class.java)
}
And here the SearchFilterDTO and FilterDTO
class SearchFilterDTO(
var currentPage: Int = 1,
var pageSize: Int = 10,
var sort: Sort? = null,
var column: String? = null,
var filters: List<FilterDTO> = ArrayList<FilterDTO>(),
var paged: Boolean = true
)
class FilterDTO(
var field: String,
var value: Any,
var comparison: Comparison
)

#Valid JSON request with BindingResult causes IllegalStateException

I have a REST service which takes a JSON request. I want to validate the JSON request values that are coming in. How can I do that?
In Spring 3.1.0 RELEASE, I know one wants to make sure they are using the latest support classes listed at 3.1.13 New HandlerMethod-based Support Classes For Annotated Controller Processing
The old ones are items like: AnnotationMethodHandlerAdapter. I want to make sure I am using the latest such as RequestMappingHandlerAdapter.
This is because I hope it fixes an issue where I see this:
java.lang.IllegalStateException: Errors/BindingResult argument declared without preceding model attribute. Check your handler method signature!
My #Controller handler method and associated code is this:
#Autowired FooValidator fooValidator;
#RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public #ResponseBody Map<String, String> fooBar(
#Valid #RequestBody Map<String, String> specificRequest,
BindingResult results) {
out("fooBar called");
// get vin from JSON (reportRequest)
return null;
}
#InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder){
binder.setValidator(fooValidator);
}
FooValidator looks like this:
#Component
public class FooValidator implements Validator {
public boolean supports(Class<?> clazz) {
out("supports called ");
return Map.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
out("validate called ");
}
private void out(String msg) {
System.out.println("****** " + getClass().getName() + ": " + msg);
}
}
If I remove the BindingResult, everything works fine except I won't be able to tell if the JSON validated.
I am not strongly attached to the concept of using a Map<String, String> for the JSON request or using a separate validator as opposed to a Custom Bean with validation annotation (How do you do that for a JSON request?). Whatever can validate the JSON request.
3.1.17 #Valid On #RequestBody Controller Method Arguments says that:
An #RequestBody method argument can be annotated with #Valid to invoke automatic validation similar to the support for #ModelAttribute method arguments. A resulting MethodArgumentNotValidException is handled in the DefaultHandlerExceptionResolver and results in a 400 response code.
In other words, if you use #Valid #RequestBody then Spring will reject an invalid request before it gets as far as calling your method. if you method is invoked, then you can assume the request body is valid.
BindingResult is used for validation of form/command objects, rather than #RequestBody.
I had to do something similar once. I just ended up making my life simpler by creating a Java object that the JSON could be convert into and used GSON to do the conversion.
It was honestly as simple as:
#Autowired
private Gson gson;
#RequestMapping(value = "/path/info", method = RequestMethod.POST)
public String myMethod(#RequestParam(value = "data") String data,
Model model,
#Valid MyCustomObject myObj,
BindingResult result) {
//myObj does not contain any validation information.
//we are just using it as as bean to take advantage of the spring mvc framework.
//data contains the json string.
myObj = gson.fromJson(data, MyCustomObject.class);
//validate the object any way you want.
//Simplest approach would be to create your own custom validator
//to do this in Spring or even simpler would be just to do it manually here.
new MyCustomObjValidator().validate(myObj, result);
if (result.hasErrors()) {
return myErrorView;
}
return mySuccessView;
}
Do all your validation in your custom Validator class:
public class MyCustomObjValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return MyCustomObj.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MyCustomObj c = (MyCustomObj) target;
Date startDate = c.getStartDate();
Date endDate = c.getEndDate();
if (startDate == null) {
errors.rejectValue("startDate", "validation.required");
}
if (endDate == null) {
errors.rejectValue("endDate", "validation.required");
}
if(startDate != null && endDate != null && endDate.before(startDate)){
errors.rejectValue("endDate", "validation.notbefore.startdate");
}
}
}
MyCustomObject does not contain any annotation for validation, this is because otherwise Spring will try to validate this fields in this object which are currently empty because all the data is in the JSON String, it could for example be:
public class MyCustomObject implements Serializable {
private Date startDate;
private Date endDate;
public Date getStartDate() {
return startDate;
}
public Date getEndDate() {
return endDate;
}
public void setStartDate(Date theDate) {
this.startDate = theDate;
}
public void setEndDate(Date theDate) {
this.endDate = theDate;
}
}
Try using the following:
#Autowired
private FooValidator fooValidator;
#InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder){
binder.setValidator(fooValidator);
}
#ModelAttribute("specificRequest")
public Map<String, String> getModel() {
return new HashMap<String, String>();
}
This will make your controller serialize the request into the type you specify it to be.
I have to say i normally dont make a service (autowired) of the validator, but it might be better.
Your handler looks like this now:
#RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public #ResponseBody Map<String, String> fooBar(
#Valid #ModelAttribute("specificRequest")
Map<String, String> specificRequest, BindingResult results) {
out("fooBar called");
// get vin from JSON (reportRequest)
return null;
}
To my knowledge this works perfectly and addresses the error you are receiving.

Categories