I send request from Angular5 to REST API (spring boot)
but spring boot can't find the value of key (it shows null),
on top image you can see the raw data that I'm sending from postman.
in spring boot I'm creating a class and field to receive data.
In controller I'm already putting the #RequestBody
Here is my controller:
#RequestMapping(value = "/edit/barcode_option", method = arrayOf(RequestMethod.POST))
fun editBarCodeOption(model: Model,
request: HttpServletRequest,
response: HttpServletResponse,
#RequestBody barcodeForm: SKBarcodeForm): ResponseEntity<*>?{
(barcodeService.editBarcodeOption(barcodeForm)).let { barcodeData ->
return responseOk("ok")
}
return responseBadRequest()
}
and the kotlin class:
class SKBarcodeForm {
var id: Long? = null
var barcodeId: String? = null
var barcodeName: String? = null
var barcodeBrand: String? = null
var isHidingProfile: Boolean? = null
var isWasteAfterUsed: Boolean? = null
var isHaveToSeparate: Boolean? = null
}
thank you for help :D
Try to remove the prefix 'is' from your boolean properties.
Related
I am bit new to spring boot and I am trying to design a search on user history which will provide 3 attributes to search user history {userId, searchKey, SearchValue}.
The search value datatype may differ based on search.
E.g
Userid=100, SearchKey=userAddress, searchValue='10 Downing Street'
Userid=100, SearchKey=external, searchValue=true
Userid=100, SearchKey=companyId, searchValue=25
I am trying to design a rest endpoint as below. This endpoint will integrate with react front end.
#GetMapping(value = "/searchUserHistoryByKeyValue")
public ResponseEntity<Object> searchUserHistoryByKeyValue(
#RequestParam(value = "userId") int userId,
#RequestParam(value = "searchKey") String searchKey,
#RequestBody Object searchValue) {
List<org.json.simple.JSONObject> entities =
userHistoryService.searchUserHisotryByKeyValue(userId, searchKey, searchValue);
return new ResponseEntity<>(entities, HttpStatus.OK);
}
I have implemented a dynamodb search on userhistory object which takes input as generic searchValue object as search filter as below.
Dynamo DB Querying - https://www.tutorialspoint.com/dynamodb/dynamodb_querying.htm
public List<JSONObject> searchUserHistoryByKeyValue(
int userId, String searchKey, Object searchValue) throws DataAccessException {
Table table = dynamoDB.getTable(userHistoryTable.getName());
Map<String, String> expressionAttributeNames =
DEFAULT_USER_FILTERS.stream()
.collect(
Collectors.toMap(attrib -> attrib, attrib -> attrib.substring(1), (a, b) -> b));
Optional<String> projectionExpression =
createProjectionExpression(
Collections.singletonList(searchKey), expressionAttributeNames);
Optional<String> filterProjectionExpression =
buildCustomProjectionExpression(
Collections.singletonList(searchKey), expressionAttributeNames);
QuerySpec querySpec =
new QuerySpec()
.withProjectionExpression(projectionExpression.orElse(StringUtils.EMPTY))
.withKeyConditionExpression("#userId = :userId")
.withFilterExpression(
String.format(
"%s = :searchValue",
filterProjectionExpression.orElseThrow(
() -> new IllegalArgumentException("Invalid Search Attributes"))))
.withNameMap(expressionAttributeNames)
.withValueMap(Map.of(":userId", userId, ":searchValue", searchValue))
.withScanIndexForward(false);
When I am trying use swagger or postman to test this endpoint , I am not able to pass in
#RequestBody Object searchValue . it just shows as empty braces - {}
Also it shows below error as -
'TypeError: Failed to execute 'fetch' on 'Window': Request with
GET/HEAD method cannot have body. '
I am not able to make this work? Appreciate your insights on this.
It's HTTP protocol.
You cannot pass any body object with the Get method. You have to use Post or Put method for using a body in HTTP request.
#RequestBody not for single value it is intended for your custom object that is used with POST or PUT but in you case you can #RequestParam also if #RequestParam take attribute required with boolean vlue which tell your endpoint caller which params is optional if you set it False and which is required if you set it True
When I invoke a repository method that makes a key search in dynamo:
"{"TableName":"music","Key":{"artist":{"S":"Henrique"},"title":{"S":"Quinta_de_sol1"}},"ConsistentRead":false}"
this is aws output:
"{}"
And when i invoke another methos that makes a find using sortKey start with:
this is sent to aws:
"{"TableName":"music","ConsistentRead":true,"KeyConditions":{"artist":{"AttributeValueList":[{"S":"Djavan"}],"ComparisonOperator":"EQ"},"title":{"AttributeValueList":[{"S":"Tempo"}],"ComparisonOperator":"BEGINS_WITH"}},"ScanIndexForward":true}"
and this is the return:
"{"Count":0,"Items":[],"ScannedCount":0}"
Steps to Reproduce the Problem
Using a entity named Music
#DynamoDBTable(tableName = "music")
data class Music(
#get:DynamoDBHashKey(attributeName = "artist")
var artist: String? = null,
#get:DynamoDBRangeKey(attributeName = "title")
var title: String? = null,
var genre: String? = null
) {
#Id
private var id: MusicId? = null
get() = MusicId(artist, title)
}
#DynamoDBDocument
data class MusicId(
#field:DynamoDBHashKey(attributeName = "artist")
var artist: String? = null,
#field:DynamoDBRangeKey(attributeName = "title")
var title: String? = null
) : Serializable
And a repository
interface MusicRepository : CrudRepository<Music, MusicId> {
fun findByArtistAndTitle(artist: String, title: String): List<Music>
fun findByArtistAndTitleStartingWith(artista: String, sortKey: String): List<Music>
}
And when i invoke:
#PostConstruct
fun init() {
println(musicRepository.findByArtistAndTitleStartingWith("Djavan", "Eu te devoro").joinToString())
}
Specifications
Lib: https://github.com/boostchicken/spring-data-dynamodb
Spring Data DynamoDB Version: 5.2.5
Spring Data Version: Doesn't used
Spring Boot Starter Web Version: 2.3.4.RELEASE
AWS SDK Version: 1.11.573
Java Version: 11
Platform Details: Windows
Someone know if is there a clean way to guarantee that the dynamo repo will never return a list that only accepts non null elements, with null elements?
To write a Spring application that invokes Amazon DynamoDB, consider using the official Amazon DynamoDB Java V2 API and the enhanced client within a Spring project.
Map items in DynamoDB tables
If you are not familiar with the AWS SDK for Java V2, please refer to this Quick Start.
I am trying to pass a String array from my typescript
tmp : Array<string> = [];
So I have a function which takes in this array as a parameter input
passValues(test : Array<string>) {
........
// some method to call post method from service
}
So in service
public passingOfValues( test : Array<string> ) : Observable<Array<string>> {
let headers = new Headers({ 'Content-Type': 'application/json'} );
let options = new RequestOptions({ headers: headers);
let response = this.http.post(this.basePath + this.modulePath + '/getArrayValue', {'test' : test }, options)
.map(this.extractData)
.catch(this.handleError);
return response;
}
But I am getting errors such as System property [org.owasp.esapi.devteam] is not set
And I read on other posts that I have to stringify the array before passing to backend.
Is there a reason why I need to stringify / also can I just pass the raw array?
EDIT 1 :
including backend controller codes
public ResponseEntity<?> getArrayValues( ArrayList<String> test ) {
logger.debug("### Test if array has a size ###" + test.size());
}
Apparently size already shows 0 from here.
EDIT 2 :
While debugging, i realised that the SQL at the back is receiving
say
HOME CHARACTER(20 OCTETS)
does this make any difference?
Like passing of string into octets or do I have to do some conversion?
Sorry if I have alot of questions am also working hard on debugging and learning more about it!
Most of the developers like JSON data as request and it's good practice in RESTful apis. why?
JSON format is {key1: value1, key2: value 2,....}
You are passing
this.http.post(this.basePath + this.modulePath + '/getArrayValue',{'test' : YOUR_ACTUAL_ARRAY})
form the front-end. The httpClient.post(url,body,options?) has url and body as mandatory. How can you get it in back-end? Since you have body only,
public ResponseEntity<?> getArrayValues(#RequestBody List<String> test) {
// codes
}
Key of passed parameter from front-end test and variable which
listens in back-end should be in same name. Otherwise
#RequestBody("KEY_NAME") List<String> any_variable
As you asked from comment, you may have two key value pairs. Eg : { "test" : value1, "tmp": value2}. Assume value1 and value2 both are String array.
this.http.post(this.basePath + this.modulePath + '/getArrayValue',{'myJson' : YOUR_JSON})
There are lot of way(Eg : Gson,ObjectMapper etc). I use another way.
Create a class called TestTmpConverter
class TestTmpConverter{
List<String> test;
List<String> tmp;
//No-argument constructors & Argument constructors
//Getters
}
In controller
public ResponseEntity<?> getArrayValues(#RequestBody List<TestTmpConverter> myJson ) {
List<TestTmpConverter> test=myJson.getTest();
List<TestTmpConverter> tmp=myJson.getTmp();
// Do your work
}
I only showed one way.There are a lot of way to pass data to back-end like #RequestParam, #PathVariable etc. I feel now you get something how you can pass the data.
For your client put your data directly on POST's body:
public passingOfValues( test : Array<string> ) : Observable<Array<string>> {
let headers = new Headers({ 'Content-Type': 'application/json'} );
let options = new RequestOptions({ headers: headers);
let response = this.http.post(this.basePath + this.modulePath + '/getArrayValue',
test, options)
.map(this.extractData)
.catch(this.handleError);
return response;
}
On your REST service use the #RequestBody annotation:
public ResponseEntity<?> getArrayValues(#RequestBody String[] test ) {
logger.debug("### Test if array has a size ###" + test.size());
}
I'm using postman and trying to pass an object in the header but getting an error for the converting from string to object... how would I do it right?
I'm attaching pictures from postman:
https://imgur.com/a/5wAxIYf
this is the code on the server:
#RequestMapping(
path= arrayOf(
"/wristbands/upload",
"/wristbands/upload/"),
method = arrayOf(RequestMethod.POST),
consumes = arrayOf(MediaType.APPLICATION_JSON_UTF8_VALUE))
open fun wristbandProcessNewAlgorithem(#RequestHeader(name = "X-V", required = true) wristbandRecords: WristbandRecordNewInputDTO): ResponseEntity<*>{
var res=wristbandProcessingService.processWristbandNewAlgorithem(wristbandRecords)
return ResponseEntity(res,HttpStatus.OK)
}
What am I doing wrong?
Thank you
Solution:
I think I found solution and it was moving the object from the header to the body and changing the code to be like this:
#RequestMapping(
path= arrayOf(
"/wristbands/upload",
"/wristbands/upload/"),
method = arrayOf(RequestMethod.POST),
headers = arrayOf("X-V"),
consumes = arrayOf(MediaType.APPLICATION_JSON_UTF8_VALUE))
open fun wristbandProcessNewAlgorithem(#RequestBody wristbandRecords: WristbandRecordNewInputDTO): ResponseEntity<*>{
var res=wristbandProcessingService.processWristbandNewAlgorithem(wristbandRecords)
return ResponseEntity(res,HttpStatus.OK)
}
In Spring Boot 1.5.4 I have a request mapping like this:
#RequestMapping(value = "/graph/{graphId}/details/{iri:.+}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public JSONObject getGraph(#PathVariable Long graphId,
#PathVariable String iri) {
log.debug("Details called for graph ID {} for IRI {}", graphId, iri);
return detailsService.getDetails(graphId, iri);
}
Accessing
http://localhost:9000/api/v1/graph/2/details/http%3Anthnth33
works fine and the server maps the request correctly and the code returns the expected result
But accessing
http://localhost:9000/api/v1/graph/2/details/http%3A%2F%2Fserverurl.net%2Fv1%2Fus%2Fh.schumacher%408tsch.net%2Fn%2FLouSchumacher
gives a bad server request (Failed to load resource: the server responded with a status of 400 (Bad Request)). The request mapping to the end point isn't even done in that case.
Obviously the slash '/' encoded as %2F (using encodeURIComponent()) causes trouble. Why? What am I missing? How should uri parameter then be encoded?
The question is not only about how to extract PathVariables but more on how to force String to recognize the correct mapping.
The issue with your example is how Spring is doing path matching. The URL you have provided as example
http://localhost:9000/api/v1/graph/2/details/http%3A%2F%2Fserverurl.net%2Fv1%2Fus%2Fh.schumacher%408tsch.net%2Fn%2FLouSchumacher
will be decoded into by container
http://localhost:9000/api/v1/graph/2/details/http://serverurl.net/v1/us/h.schumacher#8tsch.net/n/LouSchumacher
before processing by Spring matcher. This makes matche think that this only http: corresponds {iri:.+} and as later goes / so it is some longer path you don't have a mapping for.
The approach described here should work for you: Spring 3 RequestMapping: Get path value
#RequestMapping(value = "/graph/{graphId}/details/**",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public JSONObject getGraph(#PathVariable Long graphId,
HttpServletRequest request) {
String iri = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
log.debug("Details called for graph ID {} for IRI {}", graphId, iri);
return detailsService.getDetails(graphId, iri);
}