Spring Boot - Json RequestBody, String/Enum with/without quotation marks - spring

I went into a strange problem. Why does the first code accept input without quotation marks, but the second does not?
To be honest, the second one makes sense. But why is the first one accepting the input given without quotation marks?
I really would like to know why this decision was taken.
package com.example.corntest;
import lombok.extern.log4j.Log4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#SpringBootApplication
#RestController
public class CornTestApplication {
public static void main(String[] args) {
SpringApplication.run(CornTestApplication.class, args);
}
//works - does NOT remove quotation marks
//curl 'http://localhost:8080/test' -X POST -H 'Content-Type: application/json' --data-raw '"SunGoesUp"' -vv
//works - but doesnt make sense - in comp. to code made in the bottom
//curl 'http://localhost:8080/test' -X POST -H 'Content-Type: application/json' --data-raw 'SunGoesUp' -vv
#PostMapping(value = "/test", consumes = APPLICATION_JSON_VALUE)
void mytestPost(#RequestBody String myString){
System.out.println(myString);
}
enum Test {
TESTA,
TESTB,
TESTC
}
//works
//curl 'http://localhost:8080/testEnum' -X POST -H 'Content-Type: application/json' --data-raw '"TESTA"' -vv
//does not work
//curl 'http://localhost:8080/testEnum' -X POST -H 'Content-Type: application/json' --data-raw 'TESTA' -vv
//Why
#PostMapping(value = "/testEnum", consumes = APPLICATION_JSON_VALUE)
void myTestEnum(#RequestBody Test myEnumValue){
System.out.println(myEnumValue);
}
}

In the case where you are using #RequestBody String myString the payload of the http request is put in there as-is. So whatever you send will be placed in there. The content-type doesn't matter as it will copy the payload of the request as is.
In the second case, an actual JSON string is required to be able to convert to an enum. A JSON based String needs to have quotation marks else it isn't a JSON String. Something other than a string cannot be converted to an enum.
To convert from the payload of the body to the requested object Spring uses an [HttpMessageConverter]. It will select the correct HttpMessageConverter based on the Content-Type header. In your case, assuming the defaults, this will result in the MappingJackson2HttpMessageConverter. Which will use the body to convert to the enum. Which will then fail as it isn't a valid JSON string (no quotation marks).
What is a String in JSON is explained here.

Related

Golang gRPC proto unexpected token

I defined a proto in Golang shown below (and the corresponding API path) and am trying to test the POST API call as shown below, but am getting the following unexpected token error.
I verified that this error is from the award_map section (defined to be map<string, People>), in which People is an array of strings. What is the proper data format for award_map in the curl request? Thanks
Error:
{"code":3, "message":"proto: syntax error : unexpected token [","details":[]}
Post API request:
curl --header "Content-Type: application/json" \
-- request POST \
-- data '{"school_name":"MIT", "awards":["Summa-cum-laude", "Magna-cum-laude"], "award_map":{"Summa-cum-laude":[], "Magna-cum-laude": ["john", "mark"]}}' \
<POST_ENDPOINT>
Proto definitions:
message People {
repeated string names = 1;
}
message School {
string school_name = 1;
repeated string awards = 2;
map<string, People> award_map = 3;
}
The award_map values need to be full People messages, not just arrays of strings. Try this:
{
"school_name":"MIT",
"awards": [
"Summa-cum-laude",
"Magna-cum-laude"
],
"award_map": {
"Summa-cum-laude": {
"names": []
},
"Magna-cum-laude": {
"names": [
"john",
"mark"
]
}
}
}
Copy/paste version:
'{"school_name":"MIT","awards":["Summa-cum-laude","Magna-cum-laude"],"award_map":{"Summa-cum-laude":{"names":[]},"Magna-cum-laude":{"names":["john","mark"]}}}'
Note that it isn't possible to define something like map<string, repeated string>.

SpringBoot/Kotlin and Versioning through Content Negotiation: correct approach?

I have been experimenting with Content Negotiation as backend versioning for my SpringBoot/Kotlin application. I have the following:
#GetMapping("/user", produces = [MediaType.APPLICATION_JSON_VALUE])
fun getUsers() {
//some code here
}
I have found this project combining accept" header and a "Accept-Version" custom header. I wonder whether this is the correct way of implementing a content negotiation approach and if not how can I fix it?
#GetMapping("/user", produces = [MediaType.APPLICATION_JSON_VALUE], headers = ["Accept-Version=$CUSTOM_ACCEPT_HEADER"])
fun getUsers() {
//some code here
}
object VersioningUtility {
const val CUSTOM_ACCEPT_HEADER = "vnd.sample.com-v1+json"
//here more constants as each controller can be versioned independently
}
Thank you
Yes, you can implement API versioning using content negotiation by having a custom header and header value as you have specified. However, since that is not a standard header, there are other scenarios which you might have to handle by yourself, such as:
default representation when the header is not present
exception scenarios when invalid media type values are passed as part of the header.
In case you are working with only json responses, the JSON API standard for content negotiation is to send the Accept header with the value application/vnd.api+json. Since Accept is a standard request header, using that is preferred. In case you need to handle other types of responses, you can still go ahead with the custom header.
You can implement content negotiation as below:
#RestController
class UserController {
#GetMapping("/users", headers = ["Accept=${VersioningUtility.VERSION_1_HEADER}"])
fun getUser(): ResponseEntity<Any> {
return ResponseEntity(listOf(User("Abraham Lincoln")), HttpStatus.OK)
}
#GetMapping("/users", headers = ["Accept=${VersioningUtility.VERSION_2_HEADER}"])
fun getNewUser(): ResponseEntity<Any> {
return ResponseEntity(listOf(NewUser(Name("Abraham", "Lincoln"))), HttpStatus.OK)
}
}
data class User(val name: String)
data class NewUser(val name: Name)
data class Name(val firstName: String, val lastName: String)
object VersioningUtility {
const val VERSION_1_HEADER = "application/vnd.v1+json"
const val VERSION_2_HEADER = "application/vnd.v2+json"
}
The above with enable you to have 2 versions of the GET /users endpoint with the Accept header.
When the curl request is made with v1 of the header value, the response would be according to the version v1
curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.v1+json'
[
{
"name": "Abraham Lincoln"
}
]
When the curl request is made with v2 of the header value, the response would be according to the version v2
curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.v2+json'
[
{
"name": {
"firstName": "Abraham",
"lastName": "Lincoln"
}
}
]
When an invalid header value is sent, it would respond with a 406 Not Acceptable
curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.abc+json'
{
"timestamp": "2020-04-01T18:33:16.393+0000",
"status": 406,
"error": "Not Acceptable",
"message": "Could not find acceptable representation",
"path": "/users"
}
When no Accept header is sent, it would respond with the default version, ie v1 here
curl -L -X GET 'http://localhost:8080/users'
[
{
"name": "Abraham Lincoln"
}
]
Even GitHub has implemented versioning with content negotiation in a similar way and you can have a look at that in their documentation.

SpringBoot: Using #RequestParam and #RequestBody together

It looks like there are a few questions on this topic, but many are now out of date, or are asking reasonably different things.
I am using SpringBoot and the #RestController annotation.
I have a simple use case. I want to send a JSON string to a REST endpoint, that also contains a RequestParameter.
For example, I want to do
curl -d '{ "name": "Joe Bloggs" }' http://localhost:8080/test?debug=Y
I don't want to send the request parameter in the
I have a method signature that accepts both a request parameter and a request body (it's Kotlin, but I don't think that actually makes any difference here).
#PostMapping(value = ["/test"])
fun getGCP(#RequestBody json: String, #RequestParam debug: String) : String
I can access the RequestParam fine, but the RequestBody contains more than just the JSON I have sent through in the body, it contains a merge of the body and the request parameters. In the example above it would output the following for the body binding
debug=Y&{"name": "Joe Blogs"}=
Is there a way that I can simply get the RequestParameter and RequestBody as separate entities?
The problem was the content encoding of the incoming request. Changing the curl command to
curl -H "Content-Type: application/json" -d '{ "name": "Joe Bloggs" }' http://localhost:8080/test?debug=Y
Resulted in my output showing
debug=N
json={ "name": "Joe Bloggs" }

Posting XML string to WebAPI through Swagger

I have a web api method like this below "ProcessFeed".
I am using Swagger API to test this service.
The input Data needs to be a big XML string. The problem is where there is a double quote (") in the string, it is not working.
How can resolve this.
I tried making the method like this too - ProcessFeed(string data)
Code
public class InputDataModel
{
public string Data { get; set; }
}
public HttpResponseMessage ProcessFeed(InputDataModel inputDataModel)
{
var response = _processorCore.ProcessFeed(inputDataModel.Data);
}
Swagger
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \
"Data": \
"<Date>"2013-02-05"</Date> \
<Time>19:32:33.407</Time>" \
}' 'http://localhost:50545/processfeed'
Your problem is in the headers. You need to pass the Content-Type as application/xml

Android Retrofit ParseSdk Geopoint

I have some place data in parse database.
I would like to use retrofit to find closet place to given lat and longitude
Below is the link , and curl request.
https://parse.com/docs/rest/guide#geopoints-geo-queries
curl -X GET \
-H "X-Parse-Application-Id: xxxxxx" \
-H "X-Parse-REST-API-Key: xxxxx" \
-G \
--data-urlencode 'limit=10' \
--data-urlencode 'where={
"location": {
"$nearSphere": {
"__type": "GeoPoint",
"latitude": 30.0,
"longitude": -20.0
}
}
}' \
How can i convert above curl to retrofit call ?
I think i have to use QueryMap somewhere , but cannot figure out where.
This is what i have so far.
#GET("/classes/Place")
void getPlaces(#Query("limit") Integer limit,
Callback<PlacesResult> callback);
This is request with headers "X-Parse-Application-Id: xxxxxx", "X-Parse-REST-API-Key: xxxxx" and form url encoded body with #Query("where") that takes up string.
You can put these headers in http call Interceptor for all requests.
Yes, this can be done with QueryMap, which will be of two query params.

Resources