Return JSON for ResponseEntity<String> - spring-mvc

I have a method in my controller that should returns a String in JSON. It returns JSON for non primitive types:
#RequestMapping(value = "so", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> so() {
return new ResponseEntity<String>("This is a String", HttpStatus.OK);
}
The curl response is:
This is a String

The root of the problem is that Spring (via ResponseEntity, RestController, and/or ResponseBody) will use the contents of the string as the raw response value, rather than treating the string as JSON value to be encoded. This is true even when the controller method uses produces = MediaType.APPLICATION_JSON_VALUE, as in the question here.
It's essentially like the difference between the following:
// yields: This is a String
System.out.println("This is a String");
// yields: "This is a String"
System.out.println("\"This is a String\"");
The first output cannot be parsed as JSON, but the second output can.
Something like '"'+myString+'"' is probably not a good idea however, as that won't handle proper escaping of double-quotes within the string and will not produce valid JSON for any such string.
One way to handle this would be to embed your string inside an object or list, so that you're not passing a raw string to Spring. However, that changes the format of your output, and really there's no good reason not to be able to return a properly-encoded JSON string if that's what you want to do. If that's what you want, the best way to handle it is via a JSON formatter such as Json or Google Gson. Here's how it might look with Gson:
import com.google.gson.Gson;
#RestController
public class MyController
private static final Gson gson = new Gson();
#RequestMapping(value = "so", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> so() {
return ResponseEntity.ok(gson.toJson("This is a String"));
}
}

#RequestMapping(value = "so", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String so() {
return "This is a String";
}

import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
public ResponseEntity<?> ApiCall(#PathVariable(name = "id") long id) throws JSONException {
JSONObject resp = new JSONObject();
resp.put("status", 0);
resp.put("id", id);
return new ResponseEntity<String>(resp.toString(), HttpStatus.CREATED);
}

An alternative solution is to use a wrapper for the String, for instances this:
public class StringResponse {
private String response;
public StringResponse(String response) {
this.response = response;
}
public String getResponse() {
return response;
}
}
Then return this in your controller's methods:
ResponseEntity<StringResponse>

Related

Spring #RestController Get Request Content-Type to response json or html

how can I take request Content-Type value? We need this to print json response or Html respone. My code is this:
#RestController
public class GestorController {
#RequestMapping(value="/gestores", method = RequestMethod.GET)
public Object gestoresHtml(#RequestParam(value="name", required=false, defaultValue="sh14") String name) throws Exception {
String json = "prueba json";
String contentType = ?????
if(contentType.equals("application/json")){
return json;
}else{
ModelAndView mav = new ModelAndView();
mav.setViewName("gestores");
mav.addObject("name", name);
return mav;
}
}
}
Thanks for all.
Content-Type is a request header and you can get with the following code:
#RequestMapping("/display")
public void display(#RequestHeader("Content-Type") String contentType) {}
see spring's #RequestHeader docs
You don't need to do this manually. The thing that you need is Content negotiation. Which returns response type that will fit in your needs. See this post

Mismatch in #PathVariable resolving

Let's suppose to have this method signature:
#RequestMapping(value = "/verifyusers/{site}/{users}", method = RequestMethod.GET)
#ResponseBody
public List<String> verifyUser(
#PathVariable("site") String site, #PathVariable("users") String[] users) {
...
}
Receving a request like GET /verifyusers/AOUD/farmaci.rain,farmaci.postacuti
we get: site="AOUD" and users = [farmaci.rain, farmaci] that is we lose the second part of second String after dot ("postacuti")
I think it's the fault of org.springframework.util.AntPathMatcher ...
Use below code to prevent truncation of parameter after ' . '
#RequestMapping(value = "/verifyusers/{site}/{users:.+}", method = RequestMethod.GET)
#ResponseBody
public List<String> verifyUser(
#PathVariable("site") String site, #PathVariable("users") String[] users) {
...
}
Note: {users:.+}

JavaScriptDeserializer exception parsing JSON response

I am parsing a JSON response from Citrix' web services. The response looks like this
[{\"webinarKey\":123456,\"subject\":\"Subject\",\"description\":\"Webinar Description. \",\"organizerKey\":123456,\"times\":[{\"startTime\":\"2012-05-08T16:00:00Z\",\"endTime\":\"2012-05-08T17:00:00Z\"}],\"timeZone\":\"America/New_York\"}]
I manually edited that string to remove identifying information, so if there is a missing quote or anything it is unrelated.
I followed the example from this answer on SO, but still am encountering an error.
Deserializing JSON result with Json & JavaScriptSerializer
public class Webinars {
public string webinarKey;
public string subject;
public string description;
public string organizerKey;
public WebinarTimes[] times;
public string timeZone;
}
public class WebinarTimes {
public string startTime;
public string endTime;
}
JavaScriptSerializer jss = new JavaScriptSerializer();
var foo = jss.Deserialize<Webinars>(JSON);
I receive the following error: Type 'Web.Site.Webinars' is not supported for deserialization of an array.
You have to use IList<Webinars> instead of Webinars
var foo = jss.Deserialize<IList<Webinars>>(JSON);

Http Post with request content type form not working in Spring MVC 3

code snippet:
#RequestMapping(method = RequestMethod.POST)//, headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView create(#RequestBody UserAccountBean account) {
try{
accounts.put(account.assignId(), account);
}catch(RuntimeException ex)
{
return new ModelAndView("account/registerError");
}
return new ModelAndView("account/userVerification");
}
After receiving request, What I got is Http Status code 415:
The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
If I change the code to this:
code snippet:
#RequestMapping(method = RequestMethod.POST,headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView create(#RequestBody UserAccountBean account) {
try{
accounts.put(account.assignId(), account);
}catch(RuntimeException ex)
{
return new ModelAndView("account/registerError");
}
return new ModelAndView("account/userVerification");
}
I will get 405 Method not allowed. Funny thing is in the allow header of response, it lists GET and POST as allowed methods.
I do have a class that does JOSN mapping:
#Component
public class JacksonConversionServiceConfigurer implements BeanPostProcessor {
private final ConversionService conversionService;
#Autowired
public JacksonConversionServiceConfigurer(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AnnotationMethodHandlerAdapter) {
AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
HttpMessageConverter<?>[] converters = adapter.getMessageConverters();
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
MappingJacksonHttpMessageConverter jsonConverter = (MappingJacksonHttpMessageConverter) converter;
jsonConverter.setObjectMapper(new ConversionServiceAwareObjectMapper(this.conversionService));
}
}
}
return bean;
}
}
Copied from Spring examples. works great with JSON content-type.
A more general question is how to make spring mvc request handlers work with different request content-types.
Any advice would be greatly appreciated.
Unfortunately FormHttpMessageConverter (which is used for #RequestBody-annotated parameters when content type is application/x-www-form-urlencoded) cannot bind target classes (as #ModelAttribute can).
Therefore you need #ModelAttribute instead of #RequestBody. If you don't need to pass different content types to that method you can simply replace the annotation:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView create(#ModelAttribute UserAccountBean account) { ... }
Otherwise I guess you can create a separate method form processing form data with the appropriate headers attribute:
#RequestMapping(method = RequestMethod.POST,
headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView createFromForm(#ModelAttribute UserAccountBean account) { ... }
EDIT: Another possible option is to implement your own HttpMessageConverter by combining FormHttpMessageConverter (to convert input message to the map of parameters) and WebDataBinder (to convert map of parameters to the target object).
I was having HTTP response code of 415
My problems were resolved when I added Content Type to request header
e.g
"Content-Type: application/json"
At the heart of the problem, we wish to accept both application/json and application/x-www-form-urlencoded Content-types with the same request handler.
To do this, I use the #RequestBody, which was already working for application/json for me (and generally others from the threads I've found, but there is extra work so application/x-www-form-urlencoded can be used with #RequestBody.
First, create a new HttpMessageConverter capable of changing the request input to an object. I do this by reusing the FormHttpMessageConverter, which is already capable of changing the input to a MultiValueMap. I then change the MultiValueMap to a regular Map, and use Jackson to turn the Map to the desired object.
Here is the code for the HttpMessageConverter:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* <p>Converts HTTP requests with bodies that are application/x-www-form-urlencoded or multipart/form-data to an Object
* annotated with {#link org.springframework.web.bind.annotation.RequestBody} in the the handler method.
*
* #author Jesse Swidler
*/
public class ObjectHttpMessageConverter implements HttpMessageConverter<Object> {
private final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
private final ObjectMapper objectMapper = new ObjectMapper();
private static final LinkedMultiValueMap<String, String> LINKED_MULTI_VALUE_MAP = new LinkedMultiValueMap<>();
private static final Class<? extends MultiValueMap<String, ?>> LINKED_MULTI_VALUE_MAP_CLASS
= (Class<? extends MultiValueMap<String, ?>>) LINKED_MULTI_VALUE_MAP.getClass();
#Override
public boolean canRead(Class clazz, MediaType mediaType) {
return objectMapper.canSerialize(clazz) && formHttpMessageConverter.canRead(MultiValueMap.class, mediaType);
}
#Override
public boolean canWrite(Class clazz, MediaType mediaType) {
return false;
}
#Override
public List<MediaType> getSupportedMediaTypes() {
return formHttpMessageConverter.getSupportedMediaTypes();
}
#Override
public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
Map<String, String> input = formHttpMessageConverter.read(LINKED_MULTI_VALUE_MAP_CLASS, inputMessage).toSingleValueMap();
return objectMapper.convertValue(input, clazz);
}
#Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws UnsupportedOperationException {
throw new UnsupportedOperationException("");
}
}
There are many different ways a Spring app might pick up that message converter. For me, it was accomplished in an XML file:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
Using #ModelAttribute is indeed the preferred way to deal with form parameters.
Using JSON worked for me as well, I suppose it makes the JSON interpreter get the data from the body.
I was trying to use PUT though, which is a bit harder.
You can read my post about it here.
Below worked for me
On server side:
#RequestMapping(value = "test", method = RequestMethod.POST, consumes = {"application/xml", "application/json"})
#ResponseStatus(HttpStatus.OK)
public #ResponseBody
String methodName(#RequestBody EntityClassName entity) {
On client side:
String json = new JSONStringer().object()
.key("key").value("value")
.endObject()
.toString();
StringEntity se = new StringEntity(json);
se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
request.setEntity(se);
HttpResponse response = client.execute(request);
I use this code for convert html form to json .
function ConvertFormToJSON(form) {
var array = $(form).serializeArray();
var json = {};
$.each(array, function() {
json[this.name] = this.value || '';
});
return json;
}
and use single quotations was wrong . I changed ' ' to " " and problem solved.

How do i get the requestmapping value in the controller?

In the controller , i have this code,
somehow, i want to get the request Mapping value "search".
How is it possible ?
#RequestMapping("/search/")
public Map searchWithSearchTerm(#RequestParam("name") String name) {
// more code here
}
If you want the pattern, you can try HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE:
#RequestMapping({"/search/{subpath}/other", "/find/other/{subpath}"})
public Map searchWithSearchTerm(#PathVariable("subpath") String subpath,
#RequestParam("name") String name) {
String pattern = (String) request.getAttribute(
HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// pattern will be either "/search/{subpath}/other" or
// "/find/other/{subpath}", depending on the url requested
System.out.println("Pattern matched: "+pattern);
}
It seems you are looking for the path that this request has matched, then you can directly get it from servlet path
#RequestMapping("/search/")
public Map searchWithSearchTerm(#RequestParam("name") String name, HttpServletRequest request) {
String path = request.getServletPath();
// more code here
}
Having a controller like
#Controller
#RequestMapping(value = "/web/objet")
public class TestController {
#RequestMapping(value = "/save")
public String save(...) {
....
}
}
You cant get the controller base requestMapping using reflection
// Controller requestMapping
String controllerMapping = this.getClass().getAnnotation(RequestMapping.class).value()[0];
or the method requestMapping (from inside a method) with reflection too
//Method requestMapping
String methodMapping = new Object(){}.getClass().getEnclosingMethod().getAnnotation(RequestMapping.class).value()[0];
Obviously works with an in requestMapping single value.
Hope this helps.
#RequestMapping("foo/bar/blub")
public Map searchWithSearchTerm(#RequestParam("name") String name, HttpServletRequest request) {
// delivers the path without context root
// mapping = "/foo/bar/blub"
String mapping = request.getPathInfo();
// more code here
}
For Spring 3.1 and above you can use ServletUriComponentsBuilder
#RequestMapping("/search/")
public ResponseEntity<?> searchWithSearchTerm(#RequestParam("name") String name) {
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequest();
System.out.println(builder.buildAndExpand().getPath());
return new ResponseEntity<String>("OK", HttpStatus.OK);
}

Resources