I am having a piece of code like below for calling one of our service.
MultiValueMap<String, String> parametersMap = new LinkedMultiValueMap<>();
parametersMap.add("query", query);
parametersMap.add("locale", "en_US");
parametersMap.add("resultsLimit", Boolean.FALSE.toString());
parametersMap.add("maxResults", maxResults);
parametersMap.add("type", "TTT");
parametersMap.add("ids", commaSeparatedValues(ids));
parametersMap.add("infoTypes", "HHH,JJJ");
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(parametersMap, getHttpHeaders());
MyEntity myEntity = restTemplate.postForEntity("http://example.com", httpEntity, MyEntity.class);
And at the server side the controller code is like
#RequestMapping("my/service")
public MyEntity suggest(#RequestParam(required = true) String query, #RequestParam(required = true) String locale,
#RequestParam(required = false) String resultsLimit, #Valid OtherOptions options)
and the OtherOption class is like
class OtherOptions {
String maxResults;
String type;
String ids;
String infoTypes;
}
Here everything is working fine, but I am confused about somethings like .
Is it a get or post request ?
How is some of the parameter maps content become request params(query params) and some others got mapped to the Object of OtherOptions ?
Which is the actual body of the request ?
Is it a get or post request ?
It is a post request. you are calling restTemplate.postForEntity. But your server side method is not restricted as you didn't specify the method attribute for RequestMapping so same method can handle any http method from the point of server.
How is some of the parameter maps content become request params(query params) and some others got mapped to the Object of OtherOptions?
None of them are query params.
See the spring docs for the meaning of #RequestParam. In your case, it all comes from body and not as query params
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#postForEntity-java.net.URI-java.lang.Object-java.lang.Class-
The body of the entity, or request itself, can be a MultiValueMap to create a multipart request.
Which is the actual body of the request?
parametersMap is the body of the http request.
Note:
Currently your call should fail because you are posting it to http://example.com at client and listening at server side on my/service
I have seen some different conventions regarding the response status code, when making a post request using JAX-RS.
I have seen this:
Response.ok(//content or object).build();
My initial thought would be that this is wrong, since status code 200, just means ok, which is a broad term, but since we have 'Posted', I think 201 would be better since it also returns the URI of the element inside the header
Response.created(//content or object).build();
here is want i want to achieve:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createUser(String content) throws CityNotFoundException
{
ReceivedCreateUser receivedUser = gson.fromJson(content, ReceivedCreateUser.class);
User createdUser = userFacade.createUser(receivedUser.name,
receivedUser.email,
receivedUser.password,
receivedUser.city,
receivedUser.gender,
receivedUser.dateOfBirth);
if(createdUser == null){
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return Response.ok(gson.toJson(UserDTO.basic(createdUser))).build();
}
i want to add the entity to the response, but created only accepts the uri not entities.
Whether or not to return a 201 on a POST request depends greatly on the functionality behind it. If your end-point creates a new entity in the system then 201 would be the way to go, but POST is also often used to perform search request with large option sets. In this case a 200 or 204 would be desirable in case of results or no results.
But to return an entity with a POST and response 201 you could do something similar to this:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createUser(String content) throws CityNotFoundException
{
ReceivedCreateUser receivedUser = gson.fromJson(content, ReceivedCreateUser.class);
User createdUser = userFacade.createUser(receivedUser.name,
receivedUser.email,
receivedUser.password,
receivedUser.city,
receivedUser.gender,
receivedUser.dateOfBirth);
if(createdUser == null){
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return Response
.created(new Uri("http://example.org"))
.entity(gson.toJson(UserDTO.basic(createdUser)))
.build();
}
Response.ok() is not Ok all the time, it might show that the request was done but actually no resource was created. You might want to use Response.created or Response.accepted. As in this link , you can see that it all depends on the business logic of your backend.
Response type is not the best return to clients and automatic documentation like Swagger, because it doesn't have too much information. I prefer particularly to return the entity directly how to bellow:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public UserDTO createUser(String content) throws CityNotFoundException
{
ReceivedCreateUser receivedUser = gson.fromJson(content, ReceivedCreateUser.class);
User createdUser = userFacade.createUser(receivedUser.name,
receivedUser.email,
receivedUser.password,
receivedUser.city,
receivedUser.gender,
receivedUser.dateOfBirth);
if(createdUser == null){
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return UserDTO.basic(createdUser);
}
This will return a Status Code = 200, but have many ways to change to 201. My favorite is overriding the javax.ws.rs.container.ContainerResponseFilter.filter(...) as bellow:
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
#Provider
public class LocalContainerResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
if (POST.name().equals(requestContext.getMethod()) && OK.value() == responseContext.getStatus()) {
responseContext.setStatus(CREATED.value());
}
}
}
This will change JAX-RS behavior returning 201 for all POST ok.
More 2 tips:
you can change a parameter (String content) to (ReceivedCreateUser content); and
you can put the code "throw new WebApplicationException" inside userFacade.createUser(...)
I am working on part of an API, which requires making a call to another external API to retrieve data for one of its functions. The call was returning an HTTP 500 error, with description "Content type 'application/octet-stream' not supported." The call is expected to return a type of 'application/json."
I found that this is because the response received doesn't explicitly specify a content type in its header, even though its content is formatted as JSON, so my API defaulted to assuming it was an octet stream.
The problem is, I'm not sure how to adjust for this. How would I get my API to treat the data it receives from the other API as an application/json even if the other API doesn't specify a content type? Changing the other API to include a contenttype attribute in its response is infeasible.
Code:
The API class:
#RestController
#RequestMapping(path={Constants.API_DISPATCH_PROFILE_CONTEXT_PATH},produces = {MediaType.APPLICATION_JSON_VALUE})
public class GetProfileApi {
#Autowired
private GetProfile GetProfile;
#GetMapping(path = {"/{id}"})
public Mono<GetProfileResponse> getProfile(#Valid #PathVariable String id){
return GetProfile.getDispatchProfile(id);
}
The service calling the external API:
#Autowired
private RestClient restClient;
#Value("${dispatch.api.get_profile}")
private String getDispatchProfileUrl;
#Override
public Mono<GetProfileResponse> getDispatchProfile(String id) {
return Mono.just(id)
.flatMap(aLong -> {
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
return restClient.get(getDispatchProfileUrl, headers);
}).flatMap(clientResponse -> {
HttpStatus status = clientResponse.statusCode();
log.info("HTTP Status : {}", status.value());
return clientResponse.bodyToMono(GetProfileClientResponse.class);
// the code does not get past the above line before returning the error
}).map(GetProfileClientResponse -> {
log.debug("Response : {}",GetProfileClientResponse);
String id = GetProfileClientResponse.getId();
log.info("SubscriberResponse Code : {}",id);
return GetProfileResponse.builder()
// builder call to be completed later
.build();
});
}
The GET method for the RestClient:
public <T> Mono<ClientResponse> get(String baseURL, MultiValueMap<String,String> headers){
log.info("Executing REST GET method for URL : {}",baseURL);
WebClient client = WebClient.builder()
.baseUrl(baseURL)
.defaultHeaders(httpHeaders -> httpHeaders.addAll(headers))
.build();
return client.get()
.exchange();
}
One solution I had attempted was setting produces= {MediaType.APPLICATION_JSON_VALUE} in the #RequestMapping of the API to produces= {MediaType.APPLICATION_OCTET_STREAM_VALUE}, but this caused a different error, HTTP 406 Not Acceptable. I found that the server could not give the client the data in a representation that was requested, but I could not figure out how to correct it.
How would I be able to treat the response as JSON successfully even though it does not come with a content type?
Hopefully I have framed my question well enough, I've kinda been thrust into this and I'm still trying to figure out what's going on.
Are u using jackson library or jaxb library for marshalling/unmarshalling?
Try annotating Mono entity class with #XmlRootElement and see what happens.
A pleasant day.
I am having trouble with simply displaying string in raw JSON format using Postman.
This is what I have in my Java code:
#RestController
public class HeroController {
#RequestMapping(method = {RequestMethod.POST}, value = "/displayHero")
#ResponseBody
public Map<String, String> displayInfo(String name){
//System.out.println(name);
Map<String, String> imap = new LinkedHashMap<String, String>();
map.put("hero", name);
return imap;
}
}
Every time I test this in Postman, I always get null (again if I am using raw format):
{
"hero": null
}
But using form-data, on the other hand, displays just what I entered.
{
"hero": "wolverine"
}
Any information, or should do in Postman to make this raw format works instead of form-data? By the way, the raw format value is JSON(application/json), and in the Header Tab, the value of Content-Type is application/json; charset=UTF-8.
Thank you and have a nice day ahead.
Try the following code for consuming the request body as JSON, in spring boot:-
#RequestMapping(value = "/displayHero", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String displayInfo(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
// json contains the plain json string
// now you can process the json object information as per your need
// and return output as per requirements.
return json;
}
This code will accept json body of POST Request and then return it as response.
I have a method;
#POST
#Path("test")
#Consumes(MediaType.APPLICATION_JSON)
public void test(ObjectOne objectOne, ObjectTwo objectTwo)
Now I know I can post a single object in json format, just putting it into the body.
But is it possible to do multiple objects? If so, how?
You can not use your method like this as correctly stated by Tarlog.
However, you can do this:
#POST
#Path("test")
#Consumes(MediaType.APPLICATION_JSON)
public void test(List<ObjectOne> objects)
or this:
#POST
#Path("test")
#Consumes(MediaType.APPLICATION_JSON)
public void test(BeanWithObjectOneAndObjectTwo containerObject)
Furthermore, you can always combine your method with GET parameters:
#POST
#Path("test")
#Consumes(MediaType.APPLICATION_JSON)
public void test(List<ObjectOne> objects, #QueryParam("objectTwoId") long objectTwoId)
The answer is no.
The reason is simple: This about the parameters you can receive in a method. They must be related to the request. Right? So they must be either headers or cookies or query parameters or matrix parameters or path parameters or request body. (Just to tell the complete story there is additional types of parameters called context).
Now, when you receive JSON object in your request, you receive it in a request body. How many bodies the request may have? One and only one. So you can receive only one JSON object.
If we look at what the OP is trying to do, he/she is trying to post two (possibly unrelated) JSON objects. First any solution to try and send one part as the body, and one part as some other param, IMO, are horrible solutions. POST data should go in the body. It's not right to do something just because it works. Some work-arounds might be violating basic REST principles.
I see a few solutions
Use application/x-www-form-urlencoded
Use Multipart
Just wrap them in a single parent object
1. Use application/x-www-form-urlencoded
Another option is to just use application/x-www-form-urlencoded. We can actually have JSON values. For examle
curl -v http://localhost:8080/api/model \
-d 'one={"modelOne":"helloone"}' \
-d 'two={"modelTwo":"hellotwo"}'
public class ModelOne {
public String modelOne;
}
public class ModelTwo {
public String modelTwo;
}
#Path("model")
public class ModelResource {
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String post(#FormParam("one") ModelOne modelOne,
#FormParam("two") ModelTwo modelTwo) {
return modelOne.modelOne + ":" + modelTwo.modelTwo;
}
}
The one thing we need to get this to work is a ParamConverterProvider to get this to work. Below is one that has been implemented by Michal Gadjos of the Jersey Team (found here with explanation).
#Provider
public class JacksonJsonParamConverterProvider implements ParamConverterProvider {
#Context
private Providers providers;
#Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
final Type genericType,
final Annotation[] annotations) {
// Check whether we can convert the given type with Jackson.
final MessageBodyReader<T> mbr = providers.getMessageBodyReader(rawType,
genericType, annotations, MediaType.APPLICATION_JSON_TYPE);
if (mbr == null
|| !mbr.isReadable(rawType, genericType, annotations, MediaType.APPLICATION_JSON_TYPE)) {
return null;
}
// Obtain custom ObjectMapper for special handling.
final ContextResolver<ObjectMapper> contextResolver = providers
.getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);
final ObjectMapper mapper = contextResolver != null ?
contextResolver.getContext(rawType) : new ObjectMapper();
// Create ParamConverter.
return new ParamConverter<T>() {
#Override
public T fromString(final String value) {
try {
return mapper.reader(rawType).readValue(value);
} catch (IOException e) {
throw new ProcessingException(e);
}
}
#Override
public String toString(final T value) {
try {
return mapper.writer().writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new ProcessingException(e);
}
}
};
}
}
If you aren't scanning for resource and providers, just register this provider, and the above example should work.
2. Use Multipart
One solution that no one has mentioned, is to use multipart. This allows us to send arbitrary parts in a request. Since each request can only have one entity body, multipart is the work around, as it allows to have different parts (with their own content types) as part of the entity body.
Here is an example using Jersey (see official doc here)
Dependency
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey-2.x.version}</version>
</dependency>
Register the MultipartFeature
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
#ApplicationPath("/api")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("stackoverflow.jersey");
register(MultiPartFeature.class);
}
}
Resource class
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.media.multipart.FormDataParam;
#Path("foobar")
public class MultipartResource {
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postFooBar(#FormDataParam("foo") Foo foo,
#FormDataParam("bar") Bar bar) {
String response = foo.foo + "; " + bar.bar;
return Response.ok(response).build();
}
public static class Foo {
public String foo;
}
public static class Bar {
public String bar;
}
}
Now the tricky part with some clients is that there isn't a way to set the Content-Type of each body part, which is required for the above to work. The multipart provider will look up message body reader, based on the type of each part. If it's not set to application/json or a type, the Foo or Bar has a reader for, this will fail. We will use JSON here. There's no extra configuration but to have a reader available. I'll use Jackson. With the below dependency, no other configuration should be required, as the provider will be discovered through classpath scanning.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey-2.x.version}</version>
</dependency>
Now the test. I will be using cURL. You can see I explicitly set the Content-Type for each part with type. The -F signifies to different part. (See very bottom of the post for an idea of how the request body actually looks.)
curl -v -X POST \
-H "Content-Type:multipart/form-data" \
-F "bar={\"bar\":\"BarBar\"};type=application/json" \
-F "foo={\"foo\":\"FooFoo\"};type=application/json" \
http://localhost:8080/api/foobar
Result: FooFoo; BarBar
The result is exactly as we expected. If you look at the resource method, all we do is return this string foo.foo + "; " + bar.bar, gathered from the two JSON objects.
You can see some examples using some different JAX-RS clients, in the links below. You will also see some server side example also from those different JAX-RS implementations. Each link should have somewhere in it a link to the official documentation for that implementation
Jersey example
Resteasy example
CXF example
There are other JAX-RS implementations out there, but you will need to find the documentation for it yourself. The above three are the only ones I have experience with.
As far as Javascript clients, most of the example I see (e.g. some of these involve setting the Content-Type to undefined/false (using FormData), letting the Browser handle the it. But this will not work for us, as the Browser will not set the Content-Type for each part. And the default type is text/plain.
I'm sure there are libraries out there that allow setting the type for each part, but just to show you how it can be done manually, I'll post an example (got a little help from here. I'll be using Angular, but the grunt work of building the entity body will be plain old Javascript.
<!DOCTYPE html>
<html ng-app="multipartApp">
<head>
<script src="js/libs/angular.js/angular.js"></script>
<script>
angular.module("multipartApp", [])
.controller("defaultCtrl", function($scope, $http) {
$scope.sendData = function() {
var foo = JSON.stringify({foo: "FooFoo"});
var bar = JSON.stringify({bar: "BarBar"});
var boundary = Math.random().toString().substr(2);
var header = "multipart/form-data; charset=utf-8; boundary=" + boundary;
$http({
url: "/api/foobar",
headers: { "Content-Type": header },
data: createRequest(foo, bar, boundary),
method: "POST"
}).then(function(response) {
$scope.result = response.data;
});
};
function createRequest(foo, bar, boundary) {
var multipart = "";
multipart += "--" + boundary
+ "\r\nContent-Disposition: form-data; name=foo"
+ "\r\nContent-type: application/json"
+ "\r\n\r\n" + foo + "\r\n";
multipart += "--" + boundary
+ "\r\nContent-Disposition: form-data; name=bar"
+ "\r\nContent-type: application/json"
+ "\r\n\r\n" + bar + "\r\n";
multipart += "--" + boundary + "--\r\n";
return multipart;
}
});
</script>
</head>
<body>
<div ng-controller="defaultCtrl">
<button ng-click="sendData()">Send</button>
<p>{{result}}</p>
</div>
</body>
</html>
The interesting part is the createRequest function. This is where we build the multipart, setting the Content-Type of each part to application/json, and concatenating the stringified foo and bar objects to each part. If you are unfamiliar multipart format see here for more info. The other interesting part is the header. We set it to multipart/form-data.
Below is the result. In Angular I just used the result to show in the HTML, with $scope.result = response.data. The button you see was just to make the request. You will also see the request data in firebug
3. Just wrap them in a single parent object
This option should be self explanatory, as others have already mentioned.
The next approach is usually applied in this kind of cases:
TransferObject {
ObjectOne objectOne;
ObjectTwo objectTwo;
//getters/setters
}
#POST
#Path("test")
#Consumes(MediaType.APPLICATION_JSON)
public void test(TransferObject object){
// object.getObejctOne()....
}
You can't put two separate objects in one single POST call as explained by Tarlog.
Anyway you could create a third container object that contains the first two objects and pass that one within the POS call.
I have also faced with these problem. Maybe this will help.
#POST
#Path("/{par}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Object centralService(#PathParam("par") String operation, Object requestEntity) throws JSONException {
ObjectMapper objectMapper=new ObjectMapper();
Cars cars = new Cars();
Seller seller = new Seller();
String someThingElse;
HashMap<String, Object> mapper = new HashMap<>(); //Diamond )))
mapper = (HashMap<String, Object>) requestEntity;
cars=objectMapper.convertValue(mapper.get("cars"), Cars.class);
seller=objectMapper.convertValue(mapper.get("seller"), Seller.class);
someThingElse=objectMapper.convertValue(mapper.get("someThingElse"), String.class);
System.out.println("Cars Data "+cars.toString());
System.out.println("Sellers Data "+seller.toString());
System.out.println("SomeThingElse "+someThingElse);
if (operation.equals("search")) {
System.out.println("Searching");
} else if (operation.equals("insertNewData")) {
System.out.println("Inserting New Data");
} else if (operation.equals("buyCar")) {
System.out.println("Buying new Car");
}
JSONObject json=new JSONObject();
json.put("result","Works Fine!!!");
return json.toString();
}
*******CARS POJO********#XmlRootElement for Mapping Object to XML or JSON***
#XmlRootElement
public class Cars {
private int id;
private String brand;
private String model;
private String body_type;
private String fuel;
private String engine_volume;
private String horsepower;
private String transmission;
private String drive;
private String status;
private String mileage;
private String price;
private String description;
private String picture;
private String fk_seller_oid;
} // Setters and Getters Omitted
*******SELLER POJO********#XmlRootElement for Mapping Object to XML or JSON***
#XmlRootElement
public class Seller {
private int id;
private String name;
private String surname;
private String phone;
private String email;
private String country;
private String city;
private String paste_date;
}//Setters and Getters omitted too
*********************FRONT END Looks Like This******************
$(function(){
$('#post').on('click',function(){
console.log('Begins');
$.ajax({
type:'POST',
url: '/ENGINE/cars/test',
contentType: "application/json; charset=utf-8",
dataType: "json",
data:complexObject(),
success: function(data){
console.log('Sended and returned'+JSON.stringify(data));
},
error: function(err){
console.log('Error');
console.log("AJAX error in request: " + JSON.stringify(err, null, 2));
}
}); //-- END of Ajax
console.log('Ends POST');
console.log(formToJSON());
}); // -- END of click function POST
function complexObject(){
return JSON.stringify({
"cars":{"id":"1234","brand":"Mercedes","model":"S class","body_type":"Sedan","fuel":"Benzoline","engine_volume":"6.5",
"horsepower":"1600","transmission":"Automat","drive":"Full PLag","status":"new","mileage":"7.00","price":"15000",
"description":"new car and very nice car","picture":"mercedes.jpg","fk_seller_oid":"1234444"},
"seller":{ "id":"234","name":"Djonotan","surname":"Klinton","phone":"+994707702747","email":"email#gmail.com", "country":"Azeribaijan","city":"Baku","paste_date":"20150327"},
"someThingElse":"String type of element"
});
} //-- END of Complex Object
});// -- END of JQuery - Ajax
It can be done by having the POST method declared to accept array of objects. Example like this
T[] create(#RequestBody T[] objects) {
for( T object : objects ) {
service.create(object);
}
}
Change #Consumes(MediaType.APPLICATION_JSON)
to #Consumes({MediaType.APPLICATION_FORM_URLENCODED})
Then you can putting multiple objects into the body
My solution is written for CXF, it appears to be quite simple.
import org.apache.cxf.jaxrs.ext.multipart.Multipart;
#POST
#Path("paramTest")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public GenericResult paramTest(
#Multipart(value = "myData", type=MediaType.APPLICATION_JSON)
ObjectOne myData,
#Multipart(value = "infoList", type=MediaType.APPLICATION_JSON)
ObjectTwo[] infoList);
The test code for this with io.restassurred:
#Test
public void paramTest()
{
String payload1 = "" +
"{ \"name\": \"someName\", \"branch\": \"testBranch\" }";
String payload2 =
" [ { \"name\": \"cn\", \"status\": \"ts\" }," +
"{ \"name\": \"cn2\", \"status\": \"ts2\" } ] ]";
RestAssured.
given().
contentType("multipart/form-data").
multiPart("myData", payload1, "application/json").
multiPart("infoList", payload2, "application/json").
post(String.format("%s/paramTest", API_PATH)).
then().
statusCode(HttpStatus.SC_OK).
contentType(ContentType.JSON).
body("success", Matchers.equalTo(true));
}