Handle Exceptions when using #RequestHeader in spring application - java

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.

Related

How to retrieve a token from a request

I understand that there is always a token that basically glued to the request, now i would like to get retrieve a token and stuck, would appreciate some thoughts. Thank you
#GetMapping("/current")
public ResponseEntity<UserDto> getCurrent() {
return ResponseEntity.ok(something);
}
in the method body i would probably implement another service where constructor is taking token as an argument for example then does some equality checks.
In your controller accept HttpServletRequest. then you can extract any header from it.
#GetMapping("/current")
public ResponseEntity<UserDto> getCurrent(HttpServletRequest request) {
String token = request.getHeader("Authorization");
return ResponseEntity.ok(something);
}
If you don't want to take HttpServletRequest from controller you can Autowire it like this
#Autowired
private HttpServletRequest request;
or do the following
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Your question is quite hard to understand. Your "token" could be anything.
I'll try to answer your question in general:
For example, you have some option which is available only for registered users. If you are using jwt security, you generate tokens for users and "glue" them to request/response headers
For example (that's my current jUnit test):
protected MockHttpServletRequestBuilder getAuth(String url) {
return MockMvcRequestBuilders.get(url).header("Authorization", this.token);
}
And token is generated using jwt security, and after it's generated I can "glue" it to the headers. And I can just check what's in header and get token value.
So to check or get this token, you have to check what's in your header, just that simple :)
That's very general example. But the main idea is:
If you add something - you can get something. But if you haven't generated a token - you wouldn't get one "by default", because there is none, java do not generate tokens to any request. It's something you must first create first.
If you are uncommon with what I am talking about - please start from here:
https://jwt.io/introduction/

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 { }

POST doesn't work Spring 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?

How to return proper error response for content-type text/csv?

I have a servlet where in general I return text/csv as response. So a plain comma separated string.
BUT: in case of exceptions, I'd want to show just some error text as response. But instead Spring generates a custom ResponseEntity object, then tries to convert this response to csv which obviously fails.
Is it possible to replace the requested format to format=json, and then just return the default Spring error response?
#RestController
public class CsvServlet {
#RequestMapping(value = "/test", produces = "text/csv")
#ResponseBody
public String errorCsv(HttpServletRequest request) {
return "some, plain, text";
}
}
Usage: localhost:8080/test?format=csv
When having spring.security.enabled=true, this will first validate the basic auth credentials. If they fail, spring will automatically redirect to /error servlet.
Thereby BasicErrorController.error() method comes in, catching the error and generating a ResponseEntity with error attributes like timestamp, exception, path, etc.
Problem: now an Object of type ResponseEntity is returned. This is fine as long as the format parameter is either ?format=json/xml. As the response can then be properly converted.
But in my special case, where I request ?format=csv, the conversation will fail, leading to:
Response could not be created:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not
find acceptable representation
This is partially true, moreover misleading because the user should directly see that the authentication credentials have been invalid. Because in general I'm accepting csv, but Spring and the mappers don't know how to convert a ResponseEntity to a plain format like csv.
Question: how can I preserve the original exception? So that I could just return a plain text error message. It would also be fine if I could return a application/json response in this case!
Workaround as follows for the moment: override the /error servlet handler, and if the ?format parameter is not either json/xml, just return the original http status error without a body.
Thus spring cannot fail on converting the body into plain format and return the response correctly (but without exception details in the body).
#Controller
public class WorkaroundBasicErrorController extends BasicErrorController {
//default constructor from ErrorMvcAutoConfiguration
public DefaultBasicErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties,
ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
super(errorAttributes, serverProperties.getError(), errorViewResolversProvider.getIfAvailable());
}
#RequestMapping
#ResponseBody
#Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
String format = request.getParameter("format");
return (StringUtils.containsAny(format, "json", "xml"))
? super.error(request)
: new ResponseEntity<>(getStatus(request)); //neglect body for plain formats
}
}
Try to use a void #ExceptionHandler, and write error message directly in HttpResponse

Getting exception when requesting XSRF-TOKEN using Spring RestTemplate

I'm trying to call a service which has CSRF enabled and all it's endpoints are configured to request authentication header from the user.
I'm using Spring RestTemplate as follows:
ResponseEntity<String> responseEntity = getRestTemplate().exchange(
"localhost:9090/",
"HEAD",
entity,
String.class);
return responseEntity.getBody();
However, I'm not able to read the Headers from the response as I'm getting HTTP 401 error.
My workaround is to read the token from the exception that RestTemplate throws HttpClientErrorException. Like this:
exception.getResponseHeaders().get("Set-Cookie");
for (String header : headers) {
if (header.startsWith("XSRF-TOKEN")) {
token = header.split("=")[1];
break;
}
}
Is there any way to get XSRF-TOKEN token with out relying on reading it from the exception?
You are not getting an exception when accessing with GET method. Hence, I would create a get endpoint for retrieving the token and then use it for next POST calls.
Hope that approach makes sense.
the csrf only blocks requests of type post, put, delete ... that is, the get is free, therefore in order to obtain the token, first you have to make a request to a get method and extract the token from there that you would use to the next requests.
in case the token is not generated, add this to the configure of your security configuration:
http.csrf (). csrfTokenRepository (CookieCrsfTokenRepository.withHttpOnlyFalse) .any () ........
XSRF-TOKEN following spring specification is marker for header by default. So you should try get it in this way:
List tokenList = responseEntity.getHeaders().get("XSRF-TOKEN");
This collection consist of single element as usual, so first element should be your token.

Categories

Resources