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

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

Related

POST gets null value

I don't know the exact format of the incoming JSON and have no control of it. I eventually need an object to match the incoming JSON format.
My thought was to treat it as a string and write it to a file, whatever format it is, so I can examine it and create an object to match.
From Fiddler, I'm sending this:
URL: https://localhost:44351/api/values
Headers
User-Agent: Fiddler
Host: localhost:44351
Content-Length: 13
Content-Type: application/json
Body
{name='test'}
'Get' works and returns properly. 'Post' gets called but when I debug it, it gets a null value for the string.
It creates the file but, understandably, it is empty.
public class ValuesController: ApiController
{
public string Get(int id)
{
return "value=" + id.ToString();
}
public IHttpActionResult Post([FromBody] string value)
{
File.WriteAllText(#"C:\temp\Import.txt", value);
return Ok();
}
or
public IHttpActionResult Post([FromBody] GenericText value)
{
File.WriteAllText(#"C:\temp\Import.txt", value.Name);
return Ok();
}
public class GenericText
{
public string Name { get; set; }
}
Routing
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
How do I get access to the incoming JSON so I can write it out to the file?
You are posting a json object: {name='test'} and expect the body to be of type string in the controller. There are two possible solutions to your problem:
post a string and keep expecting a string in the controller.
keep posting the json object with the property name of type string, and change the expected type in the controller.
which would look like:
public class MyRequestObject
{
public string Name {get;set;}
}
public IHttpActionResult Post([FromBody] MyRequestObject value)
{
File.WriteAllText(#"C:\temp\Import.txt", value.Name);
return Ok();
}
I never did solve this directly, but was able to work around it.
If your controller descends from Controller, you get access to Request
//
// Summary:
// Gets the HttpRequestBase object for the current HTTP request.
//
// Returns:
// The request object.
public HttpRequestBase Request { get; }
I used it like so
value = new StreamReader(Request.InputStream).ReadToEnd();
Note that in some cases you need to reset the input stream to the beginning to be able to read any content, but I didn't need that step in the final solution.

Spring test mockmvc get request with body

From my understanding when Request Method is Get we should not have Request body, we use #RequestParam to read the url parameters. But spring test seems to be allowing it. Below is a sample code.
#Controller
public class SomeController {
#RequestMapping(value = "/abc", method = RequestMethod.GET)
public String greeting(#RequestBody Student student) {
return "Hello "+ student.name;
}
}
This is the test code and jsonString is the string representation of a Student object.
#Test
public void shouldReturnMessage() throws Exception {
this.mockMvc.perform(get("/abc").content(jsonString)).andDo(print()).andExpect(status().isOk());
}
My question here is, should Spring stop allowing #RequestBody with Get request? The above test runs fine and it's able to return me the student name. But when I try to call the same endpoint using curl it throws an error.
Reference : HTTP GET with request body

Return JSON for ResponseEntity<String>

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>

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