POST doesn't work Spring java - java

I have an web application and I'm trying to creat a simple POSt method that will have a value inside the body request:
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
#CrossOrigin(origins = "http://localhost:3000")
public #ResponseBody String updateTtl(#RequestBody long ttl) {
/////Code
}
My request which I call from some rest client is:
POST
http://localhost:8080/cachettl
Body:
{
"ttl": 5
}
In the response I get 403 error "THE TYPE OF THE RESPONSE BODY IS UNKNOWN
The server did not provide the mandatory "Content-type" header."
Why is that happening? I mention that other GET requests are working perfectly.
Thanks!
Edit:
When I tried it with postman the error message I got is "Invalid CORS request".

Spring application just doesn't know how to parse your message's body.
You should provide "header" for your POST request to tell Spring how to parse it.
"Content-type: application/json" in your case.
You can read more about http methods here: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_and_retrieving_form_data
Updated:
Just in case of debug, remove useless annotations to test only POST mechanism. Also, change types of arg and return type. And try to use case-sensitive header.
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
public void updateTtl(#RequestBody String ttl) {
System.out.println("i'm working");
}

Since the error is about the response type, you should consider adding a produces attribute, i.e :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
Since you are also consuming JSON, adding a consumes attribute won't hurt either :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)

The error message is slightly misleading. Your server code is not being hit due an authentication error.
Since you say spring-security is not in play then I suspect you're being bounced by a CORS violation maybe due to a request method restriction. The response body generated by this failure (if any at all) is automatic and will not be of the application/json type hence the client failure. I suspect if you hit the endpoint with something that doesn't care for CORS such as curl then it will work.
Does your browser REST client allow you to introspect the CORS preflight requests to see what it's asking for?

Related

GET call with request body - request body not accessible at controller

I am trying to do a get call with request body(JSON) as the request parameter list exceeds the limit. I am able to send the request via postman/insomnia and request is reaching till controller without any error. But the "requstBody" is empty at controller. What i am missing here?
#GET
#Path("\path")
#Consumes(APPLICATION_JSON)
#Produces(APPLICATION_JSON)
public Response getResponse(String requestBody) throws IOException { }
When I replaced #GET with #POST, requestBody has value. For GET call do we need to add anything more?
I am trying to do a get call with request body(JSON) as the request parameter list exceeds the limit. I am able to send the request via postman/insomnia and request is reaching till controller without any error. But the "requstBody" is empty at controller. What i am missing here?
One thing you are missing is the fact that the semantics of a request body with GET are not well defined.
RFC 7231, Section 4.3.1:
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
There are two ways for sending parameters in an Http Get method. PathVariable and RequestParam. In this way, sent parameters are visible in the request URL. for example:
www.sampleAddress.com/countries/{parameter1}/get-time?city=someValues
In the above request, parameter1 is a path variable and parameter2 is a request parameter. So an example of a valid URL would be:
www.sampleAddress.com/countries/Germany/get-time?city=berlin
To access these parameters in a java controller, you need to define a specific name for the parameters. For example the following controller will receive this type of requests:
#GetMapping(value = "/countries/{parameter1}/get-time", produces = "application/json; charset=utf-8")
public String getTimeOfCities(
#PathVariable(value = "parameter1") String country,
#RequestParam(value = "city") String city
){
return "the method is not implemented yet";
}
You are able to send RequestBody through a Get request but it is not recommended according to this link.
yes, you can send a body with GET, and no, it is never useful
to do so.
This elaboration in elasticsearch website is nice too:
The HTTP libraries of certain languages (notably JavaScript) don’t allow GET requests to have a request body. In fact, some users are suprised that GET requests are ever allowed to have a body.
The truth is that RFC 7231—the RFC that deals with HTTP semantics and
content—does not define what should happen to a GET request with a
body! As a result, some HTTP servers allow it, and some—especially
caching proxies—don’t.
If you want to use Post method, you are able to have RequestBody too. In the case you want to send data by a post request, an appropriate controller would be like this:
#PostMapping(value = "/countries/{parameter1}/get-time", produces = "application/json; charset=utf-8")
public String getTimeOfCitiesByPost(
#PathVariable(value = "parameter1") String country,
#RequestParam(value = "city") String city,
#RequestBody Object myCustomObject
){
return "the method is not implemented yet";
}
myCustomObject could have any type of data you defined in your code. Note that in this way, you should send request body as a Json string.
put #RequestBody on String requestBody parameter
#RequestMapping("/path/{requestBody}")
public Response getResponse(#PathVariable String requestBody) throws IOException { }

Content-type and #ResponseBody in spring

It could be very simple but it will be very helpful for me to understand...
I used #ResponseBody in my restcontroller to return String value to browser. The response string is successfully received in browser.
ie:
#RequestMapping(value="/foo", method=RequestMethod.GET)
#ResponseBody
public String foo() {
return "bar";
}
What is the content-type of above response? If this is going to be like writing setAttribute in servlet response what could the attribute name?
If the browser accept only "application/json" how spring will treat the response?
Submitted code produces text/html, as do all mapped Controller methods by default. If you want to produce application/json, you have to change your RequestMapping to
#RequestMapping(value="/foo", method=RequestMethod.GET, produces = "application/json")
However this is not a valid Json String, you would have to change it because the method you submitted would return empty body. The submitted example would be valid text/plain.
When the request contains header "Accept: application/json" and other content type is returned, Spring returns Json-type response explaining that HttpMediaTypeNotAcceptableException was thrown.
Regarding the servlet analogy - please explain, I don't fully understand what you mean. The String is returned as response body, it's very different from request attributes. What would you like to achieve?
I assume the content type will be plain/text. If the request sets accept to "application/json" it depends on your browser/tool. Most rest clients won't display it as it is not application/json. If you invoke the API directly I would assume it is displayed due to browser content sniffing (can be disabled via a header).

Spring Web MVC: no request body possible for HTTP DELETE anymore

I have a question for the developers of Spring Web MVC.
In a nutshell: previously it was possible to send a request body in an HTTP DELETE message, but now it is not possible anymore. Why?
In detail:
We are using spring-webmvc-4.2.4.RELEASE.
#RestController
public class Controller {
#RequestMapping(value = "/{pathVariable}/deleteAnything", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteAnything(#PathVariable String pathVariable,
#Valid #RequestBody Set<Pojo> pojoSet) {
...
We send
DELETE /anything/deleteAnything HTTP/1.1
Content-Type: application/json
Host: example.com
[ {
"any field" : "Any value"
} ]
and get the exception
m.m.a.RequestResponseBodyMethodProcessor : Read [java.util.Set<packagename.Pojo>] as "application/json;charset=UTF-8" with [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#333825a3]
.w.s.m.m.a.ServletInvocableHandlerMethod : Error resolving argument [1] [type=java.util.Set]
HandlerMethod details:
Controller [packagename.Controller]
Method [public org.springframework.http.ResponseEntity<?> packagename.Controller.deleteAnything(java.lang.String,java.util.Set<packagename.Pojo>)]
org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.http.ResponseEntity<?> packagename.Controller.deleteAnything(java.lang.String,java.util.Set<packagename.Pojo>)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:151)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:125)
...
It seems that the request body has been removed.
If we use HTTP POST instead of HTTP DELETE everywhere, it works fine.
Previously it worked fine (sorry that I cannot specify previously because our dependencies are very complicated. If it helps you, I can post an old build.gradle).
Why is it not possible anymore?
You probably should redesign your API, as payloads within DELETE requests should be ignored.
From https://www.rfc-editor.org/rfc/rfc7231#section-4.3.5:
A payload within a DELETE request message has no defined semantics.
From https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3:
If the request method does not include defined semantics for an
entity-body, then the message-body SHOULD be ignored when handling the
request.
It seems to be a problem with zuul. Without zuul it works. Spring has nothing to do with it.

Spring - 405 Http method DELETE is not supported by this URL

Well I have a strange problem with executing a "DELETE" HTTP request in Spring.
I have a controller method which I have mapped a DELETE request to:
#RequestMapping(value = "/{authorizationUrl}",method=DELETE)
public void deleteAuthorizationServer(
#RequestHeader(value="Authorization") String authorization,
#PathVariable("authorizationUrl") String authorizationUrl)
throws IOException {
System.out.println("TEST");
}
The controller is mapped using #RequestMapping("/authorization_servers");
When I send a request through my DEV Http Client, I am getting the response : 405 Http method DELETE is not supported by this URL.
The request looks like this:
DELETE localhost:8080/authorization_servers/asxas
Headers:
Authorization: "test:<stuff>"
If someone can look into this and help me, I would be grateful
This will work:
#RequestMapping(value = "/{authorizationUrl}", method = DELETE)
#ResponseBody
public void deleteAuthorizationServer(
#RequestHeader(value="Authorization") String authorization,
#PathVariable("authorizationUrl") String authorizationUrl
){
System.out.printf("Testing: You tried to delete %s using %s\n", authorizationUrl, authorization);
}
You were missing #ResponseBody. Your method was actually getting called; it was what happened after that that was producing the error code.
Your annotation should look like this:
#RequestMapping(value = "/{authorizationUrl}",method=RequestMethod.DELETE)
I don't know where you got that DELETE variable from. :-)
If the #RequestMapping pattern doesn't match or is invalid, it results in a 404 not found. However, if it happens to match another mapping with a different method (ex. GET), it results in this 405 Http method DELETE is not supported.
My issue was just like this one, except my requestMapping was the cause. It was this:
#RequestMapping(value = { "/thing/{id:\\d+" }, method = { RequestMethod.DELETE })
Do you see it? The inner closing brace is missing, it should be: { "/thing/{id:\\d+}" } The \\d+ is a regular expression to match 1 or more numeric digits. The braces delimit the parameter in the path for use with #PathVariable.
Since it's invalid it can't match my DELETE request:
http://example.com/thing/33
which would have resulted in a 404 not found error, however, I had another mapping for GET:
#RequestMapping(value = { "/thing/{id:\\d+}" }, method = { RequestMethod.GET })
Since the brace pattern is correct, but it's not a method DELETE, then it gave a error 405 method not supported.
I needed to return ResponseEntity<Void> (with custom response status) instead of setting custom response status on HttpServletResponse (from endpoint method param).
ex: http://shengwangi.blogspot.com/2016/02/response-for-get-post-put-delete-in-rest.html
also make sure you're calling it with "Content-Type" header="text/html". If not, then change it or specify it in the requestMapping. If it doesn't match, you get the same 405.

Handle Exceptions when using #RequestHeader in spring application

Here is my Java code that uses the Spring Framework:
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody String SampleFunction(#RequestHeader("Authorization") String details)
{
System.out.println("Authorization details recieved");
}
I am trying to access Authorization header. I want to handle the missing Authorization header by redirecting it to a 400 Bad Request page. How can I do this?
By default the header is required. So if it is missing, you will get an exception.
However, see code below. Now, if it is missing, the details string will be null.
#RequestHeader(required = false, value = "Authorization") String details
If the header value is missing by default the response is 400 Bad Request. You just need to configure redirecting.

Categories

Resources