Serve JSON "as is" from Spring controller using content negotiation - json

In my webapp, all my message converters are in place, and if I change getContent below to return a bean/pojo, it returns as " application/json;charset=UTF-8", which is expected, but I now want to serve JSON "as is".
E.g. I have a simple stub web service with which users can PUT a blob of JSON content which is persisted somewhere, and then an equivalent GET call to read it back.
#Controller
public class StubController {
#Autowired
#Qualifier("keyValueStore")
private KVStore kv;
#RequestMapping(value = "/stub/{id}", method = RequestMethod.GET)
public #ResponseBody
String getContent(#PathVariable("id") final String id) {
return kv.get(id);
}
#RequestMapping(value = "/stub/{id}", method = RequestMethod.PUT)
public String putContent(#PathVariable("id") final String id, #RequestBody String body) {
kv.set(id, body);
return "redirect:/stub/"+id;
}
}
However, the getter returns header "Content-Type: text/html;charset=UTF-8" if I call http://host/stub/123.json in the browser. My guess that this is happening is because I'm not returning anything that is "converted" by the Jackson converter, hence the return header isn't modified.
I need it to be application/json -- any ideas what to do? Perhaps an annotation with which I can specify the return headers?

I managed to get around this by adding an HttpServletResponse param to my getContent() method and setting the content type directly.
http://forum.springsource.org/showthread.php?t=97140
#RequestMapping(value = "/stub/{id}", method = RequestMethod.GET)
public #ResponseBody String getContent(#PathVariable("id") final String id, HttpServletResponse response) {
response.setContentType("application/json");
return kv.get(id);
}

Related

How to return JSON from serialized JSON in Spring Rest Controller

I have a String that represents JSON:
[{"type":"ERROR","message":"some error message"}]
This String is returned by ex.contentUTF8() in the following code:
#RestControllerAdvice
#RequiredArgsConstructor
public class ControllerExceptionHandler {
#ExceptionHandler(FeignException.class)
#ResponseBody
public String afterThrowing(FeignException ex, HttpServletResponse response) {
response.setStatus(ex.status());
return ex.contentUTF8();
}
}
An HTTP client though doesn't get a JSON response though. Instead, it gets HTML with body of the response being that String. How can I instead have JSON returned? Do I need to deserialize that String?
Update
The response to the client must be exactly the JSON object that represent (would be serialized into) the String returned by ex.contentUTF8():
[{"type":"ERROR","message":"some error message"}]
So, a wrapper object like ErrorDto (as provided in a few answers) will not work.
You should return an Object that fit with your JSON representation :
public class ErrorDto {
private String type;
private String message;
}
And :
#RestControllerAdvice
#RequiredArgsConstructor
public class ControllerExceptionHandler {
#ExceptionHandler(FeignException.class)
#ResponseBody
public ErrorDto afterThrowing(FeignException ex, HttpServletResponse response) {
return new ErrorDto("your type", "your message");
}
}
Building from the answer by #Fabien, you can use the ResponseEntity<Object> class to return an application/json content type response instead. Using ResponseEntity essentially gives control over every aspect of the response.
You can try something like this instead:
#ExceptionHandler(FeignException.class)
public ResponseEntity<Object> afterThrowing(FeignException ex) {
ErrorDto payload = new ErrorDto(ex.status(), ex.contenUTF8());
return ResponseEntity
.status(ex.status())
.contentType("application/json")
.body(payload);
}

Spring Boot REST multi part request (file + json) throwing 415 Unsupported Media Type exception

I am using spring boot version = 1.5.2.RELEASE.
When I am sending multi part file with json object to upload file in postman, It throwing 415 Unsupported Media Type exception.
This is my controller class.
#RestController
#RequestMapping("/service/promotion/")
public class JobController {
....
....
....
#RequestMapping(value = "/job/test", method = RequestMethod.POST, produces = "application/json", consumes = "multipart/form-data")
public ResponseEntity<Object> createJobTest(#Valid #RequestBody JobRequest jobRequest,
#RequestParam(value = "file", required = false) MultipartFile multiPartFile) throws Exception {
My json request class.
public class JobRequest {
private String campaignKey;
private String communicationId;
private Integer channelId;
private String templateType;
private String subject;
private String frequencyControl;
private Integer leadsRequested;
private String keywordRelavance;
private String scheduledAt;
private String file;
private String updatedBy;
//getter and setter
}
Json request in postman
Multipart file request in postman
Header Content-type
But when I removed consumes from controller class and from postman as well like
#RequestMapping(value = "/job/test", method = RequestMethod.POST, produces = "application/json")
then debugger coming in controller class but multi part file value coming
null in request object like
I googled a lot there are many similar questions which already posted but none of them helped me.
Please help me to sort out this mystery.
Thank you.
Check this File upload along with other object in Jersey restful web service
Another way is you can pass whole object in text like you are passing file in form-data and convert in object.
#RequestMapping(value = "/uploadDocs", method = RequestMethod.POST, produces = "application/json", consumes = "multipart/form-data")
public ResponseEntity<Object> methodName(#RequestParam("files") MultipartFile file, #RequestParam("anyKeyName") String objectString)
Than you can convert string to object using
Class object = new ObjectMapper().readValue(objectString, Class.class);

How to create a POST request in REST to accept a JSON input?

I am trying to learn RESTful web services. And am creating a simple set of web services. Got stuck when I started working on POST.
I want to pass JSON input to a POST method. This is what I did in the code:
#RequestMapping(value = "/create", method = RequestMethod.POST, consumes="application/x-www-form-urlencoded", produces="text/plain")
#ResponseStatus(HttpStatus.CREATED)
public #ResponseBody String createChangeRequest(MyCls mycls) {
return "YAHOOOO!!";
}
I included Jackson in my POM.xml.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-lgpl</artifactId>
<version>1.9.13</version>
</dependency>
MyCls is a simple class with a few getters and setters.
I am calling the above POST service from chrome's simple REST client.
URL: http://localhost:8080/MYWS/cls/create
Data: {<valid-json which corresponds to each variable in the MyCls pojo}
I see the below response:
415 Unsupported Media Type
The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
I tried adding header as "application/json" in the POST request in the REST client - but that did not help.
Can someone let me know what I am missing here? How can I automatically map my input JSON to the MyCls pojo? Am I missing any configuration here?
Edit:
MyCls.java
public class MyCls{
private String name;
private String email;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
name= name;
}
---similar getter and setter for email, address--
}
json from chrome Simple REST Client:
{"name":"abc", "email":"de#test","address":"my address"}
Edit:
Changed my controller method to the following, but still see the same error:
#RequestMapping(value = "/create", method = RequestMethod.POST, consumes="application/json", produces="text/plain")
#ResponseStatus(HttpStatus.CREATED)
public #ResponseBody String createChangeRequest(#RequestBody MyCls mycls) {
return "YAHOOOO!!";
}
Assuming your client is sending application/json as its content type, then a handler mapped to
consumes="application/x-www-form-urlencoded"
will not be able to handle it. The actual Content-type doesn't match the expected.
If you are expecting application/json, you should instead have
consumes="application/json"
Also, declaring
public #ResponseBody String createChangeRequest(MyCls mycls) {
is (in default environment) equivalent to
public #ResponseBody String createChangeRequest(#ModelAttribute MyCls mycls) {
This means the MyCls object is created from request parameters, not from JSON body. Instead, you should have
public #ResponseBody String createChangeRequest(#RequestBody MyCls mycls) {
so that Spring deserializes your JSON to an object of type MyCls.

Backbone.js Collection.sync & spring endpoints

I have a backbone.js client app and Spring on the app tier. In Backbone I'm using a Collection of 'ServerConfig' objects... the URL of the backbone collection points to a spring endpoint.
The 'GET' request returns an ArrayList of a POJO called 'ServerRuntimeConfig' which ultimately ends up returning a JSON response with the following response body:
[{"useIUX":true,"serverDomainName":"localhost","selected":true}]
The 'PUT' request, which is called when I do a 'sync' on the collection sends the following JSON request with the below request body (same as the GET):
[{"useIUX":true,"serverDomainName":"localhost","selected":true}]
The problem is that I can't get spring to automatically map the JSON collection into a collection of POJOs on the PUT... so I've had to do it manually.
Here is the GET end point
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.GET,
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigRequest(
HttpServletRequest request,
HttpServletResponse response) {
// first grab all of the hostnames from the properties file
ArrayList<String> allServersDomains = new ArrayList();
List<String> servers = Arrays.asList(
StringUtils.tokenizeToStringArray(
this.getAppProperties().get("serverList"), ","));
allServersDomains.addAll(servers);
MyTTJMXClient jmxClient = new MyTTJMXClient();
return jmxClient.readRemoteConfigurations(allServersDomains);
}
Here is the PUT endpoint
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.PUT,
consumes = "application/json",
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
#RequestBody String body,
HttpServletRequest request,
HttpServletResponse response) {
String jsonSource = body;
ObjectMapper mapper = new ObjectMapper();
ArrayList<ServerRuntimeConfig> serverConfigs = new ArrayList();
try {
serverConfigs = mapper.readValue(jsonSource,
new TypeReference<ArrayList<ServerRuntimeConfig>>() { } );
} catch (IOException e) {
e.printStackTrace();
}
MyTTJMXClient jmxClient;
jmxClient = new MyTTJMXClient();
return jmxClient.setRemoteConfigurations(serverConfigs);
}
Is there a way to avoid the manual Jackson mapping I'm doing in the PUT endpoint? I tried the following endpoint signature but serverConfigs is always null
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.PUT,
consumes = "application/json",
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
Again, the manual Jackson mapping does the trick, but it's not using the awesomeness that is Spring.
Has anyone else out there built a Spring end point that automatically maps Backbone/JSON collections to POJOs?
Assuming the mapping you've used in your PUT method works,
Try to change your method signature from:
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
to:
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
#RequestBody ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
The #RequestBody annotation is the important part here. It will automatically use spring's converter (in this case, jackson) to populate the variable.

Java Filter: Writing object to response having "marshalled" format

I am using Spring Delegating proxy filter to do some validations in Filter class before passing on to Controller. On failed validations i intend to return user defined "ErrorMessage" object in json format, what would be the best way to return json from filter? Is it possible? I tried returning string by writing it in output and it worked fine but i dont seem to able to figure out how to return object and that too in json format
Code Snippet:
application context xml -
<bean class="com.company.rest.ValidationFilter" id="validationFilter" />
ValidationFilter -
public class ValidationFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
final String acceptHeader = httpServletRequest.getHeader("Accept");
if (PAYLOAD_FORMATS.jsonp.getContentType().equalsIgnoreCase(acceptHeader)) {
final OutputStream outputStream = httpServletResponse.getOutputStream();
final GenericResponseWrapper wrapper = new GenericResponseWrapper(httpServletResponse);
//Some validation
if (ifValidationFails) {
httpServletResponse.setStatus(400);
httpServletResponse.addHeader("cause", "Required String parameter is not present.");
//Need to return this object in response
final ExceptionWrapper exception = new ExceptionWrapper();
exception.setErrorMessage("Required String parameter is not present");
//Returning string works fine
outputStream.write(new String("Required String parameter is not present").getBytes());
} else {
chain.doFilter(request, wrapper);
outputStream.write(new String("test" + "(").getBytes());
outputStream.write(wrapper.getData());
outputStream.write(new String(");").getBytes());
}
wrapper.setContentType("text/javascript;charset=UTF-8");
outputStream.close();
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
}
Filters are part of the Servlet API which is quite low level - Spring MVC builds on that standard. You don't get all the goodies that the Spring guys have written to automatically serialize POJOs. Although I imagine it would be possible to manually wire together the necessary components necessary to do it.
I suggest looking at the declartive validation that exists using the #Valid annotation in Spring MVC: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html#validation-beanvalidation