No cache header in Spring Boot response from RestController - java

I have a typical controller which returns JSON and I need to return no-cache header as a part of a response. Please note at this moment I have no spring security on classpath, however if there is need to make it to work out-of-the-box then I'm fine with this.
Currently I have implemented this in the following manner:
public static <T> ResponseEntity<T> createResponseSpringWay(final T body) {
org.springframework.http.CacheControl cacheControl = org.springframework.http.CacheControl.noCache();
return ResponseEntity.ok().cacheControl(cacheControl).body(body);
}
Is there any spring boot property that can make it automatically for me? I want to get rid of ResponseEntity at all methods.
I've tried the following configuration, but I believe it only applies to static resources, so it doesn't work:
spring.resources.cache.cachecontrol.no-store=true
spring.resources.cache.cachecontrol.must-revalidate=true
spring.resources.cache.cachecontrol.no-cache=true

Related

Spring boot PutMapping with Enum as RequestBody issue

I have a spring boot controller endpoint as follows.
#PutMapping("/manage/{id}")
public ResponseEntity<Boolean> manage(#PathVariable Long id, #RequestBody Type type) {
...
}
Where Type is an Enum as follows.
public enum Type {
ONE,
TWO
}
ISSUE 1: When I test this controller, I have to send the content as "ONE" instead of ONE for a successful invocation. i.e. it works with the following code.
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("\"" + Type.ONE + '\"'))
.andExpect(status().isOk());
It does not work with
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(Type.ONE.name()))
.andExpect(status().isOk());
ISSUE 2: I am not able to invoke this method from the Angular service.
this.http.put<string>('/api/manage/' + id, type)
gives me
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
Everything works when I add the Enum to a Dto and send an object from the client. But due to some business requirements, I want to use the current structure itself. i.e the Enum as a RequestBody.
UPDATE
I also tried to change the controller method structure to
#PutMapping(value = "/manage/{id}", consumes = MediaType.TEXT_PLAIN_VALUE)
I get the following error.
Content type 'text/plain' not supported
Both issues stem from trying to use a JSON endpoint as a plain text endpoint.
Ad 1, ONE is invalid JSON ("ONE" is valid)
Ad 2, when you just post a string, it is sent as text/plain and the endpoint complains.
Probably adding consumes="text/plain" to your #PutMapping will solve the problem, but frankly - I am not sure if string/enum mappings work out-of-the-box in the hodge-podge that is spring boot.

#FeignClient forces #GetMapping with #RequestBody to POST

I have following REST controller with GET method that have BODY, that works fine with tests and postman
#RestController
#RequestMapping(value = "/xxx")
public class Controller {
#GetMapping({"/find"})
public LocalDateTime findMax(#RequestBody List<ObjectId> ids) {
//return sth
}
}
but when FeignClient is used to call service, instead GET request a POST request is generated (#GetMapping annotation is ignored)
#FeignClient
public interface CoveragesServiceResource extends CoveragesService {
#GetMapping({"/find"})
LocalDateTime findMax(#RequestBody List<ObjectId> ids);
}
that gives an error:
Request method 'POST' not supported
GET request technically can have body but the body should have no meaning as explained in this answer. You might be able to declare a GET endpoint with a body but some network libraries and tools will simply not support it e.g. Jersey can be configured to allow it but RESTEasy can't as per this answer.
It would be advisable to either declare /find as POST or don't use #RequestBody.

Configure Spring interceptor for controllers only

I'm trying to configure a Spring interceptor for controllers only in the following way. For the beginning I want to exclude all the requests starting with /swagger. I try to do it in the following way:
registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/swagger**");
However, interceptor gets fired. Where is a mistake?
Maybe, there is an alternative solution with #ControllerAdvice. But I need to get request headers, so I guess it doesn't fit my needs.
Thanks for any help!
Try to use "/swagger*/**" or "/swagger*" instead of "/swagger**"
I solved the problem in the following way:
#ControllerAdvice
public class SomeAdvice {
#ModelAttribute
public void token(HttpServletRequest request) {
// getting headers and setting the attribute in the request
request.setAttribute("theAttribute", new SomeObject());
}
}
And then I get the request attribute in a controller this way:
public void someMethod(#RequestAttribute("theAttribute") SomeObject someObject) {
// some logic goes here
}
P.S. And one more note. If you're using Swagger you'll get into the trouble as Swagger will consider this attribute as controller method parameter. To ignore it you can use the following snapshot of configuration:
.ignoredParameterTypes(SomeObject.class);

Is there any way to configure spring mvc to assume a content-type?

I have a method to which I want to post some json data, that looks like this
#RequestMapping(value = "/m1", method = RequestMethod.POST)
public Object m1(#RequestBody Map<String, ?> body) {
// do something
}
This works great when I set the content-type header to application/json when I post, but fails with an error if I don't (it cannot deserialize the post body into the map because it doesn't know how)
What would I have to configure in spring to make it use application/json as a default when no header is specified?
The class that converts the JSON to your object is called an HttpMessageConverter. I assume you are using the default Jackson one that comes with Spring. You can write a custom MessageConverter, that will always return true in it's supports method with your response object type and then just call the Jackson httpconverter in your readInternal and writeInternal methods. If you do this however, be careful, as once it's registered in your requesthandler, it will be asked on all #ResponseBody and #RequestBody requests.

Null content-type when migrating from Jersey to RESTEasy.

So I wrote a sample REST resource that works like a charm in Jersey/Tomcat, but when I take it to RestEASY/Tomcat it blows. I mean really? what happened to working out of the box. Anyway a little frustrated. I get this error when trying to access the resource(http://localhost:7070/mg/mytest)
"content-type was null and expecting to extract a body"
7842 [http-7070-2] ERROR com.loyalty.mg.rest.exception.MGExceptionMapper - Error caught in the exception mapper -
org.jboss.resteasy.spi.BadRequestException: content-type was null and expecting to extract a body
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:131)
at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:98)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:121)
at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:247)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:212)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:202)
#Path("/mytest")
public class TestResource {
#GET
public Response getData()
I guess the question also is - is RestEASY any better than Jersey, this is just the start and I am getting errors. Should I just stick to Jersey?
Also already tried this as well :)
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
<param-value>json : application/json, xml : application/xml</param-value>
</context-param>
The code that throws that exception looks like this:
final MediaType mediaType = request.getHttpHeaders().getMediaType();
if (mediaType == null) {
throw new BadRequestException(
"content-type was null and expecting to extract a body");
}
The problem seems to be that RestEASY cannot figure out a content type from the headers of the request that it received. This suggests that either that the content type in the request is bogus, or that there is a problem with the way that you have configured RestEASY.
I guess the question also is - is RestEASY any better than Jersey, this is just the start and I am getting errors. Should I just stick to Jersey?
I cannot answer that. However, I think you are being too quick to blame RestEASY for something that could be your code's fault.
A classic cause of this, is if you have code like this:
#GET
#Path("/foo/{bar}")
#Produces(MediaType.TEXT_HTML)
public Response foo(#PathParam("bar") String bar) {
...and you forget to annotate the bar argument with #PathParam. Then RestEasy thinks it should be reading bar from the body of the request, instead of from the URL path, and will chuck this exception.
That doesn't seem to be what's happening in your case, but I got the same exception, and this was the cause.
RestEASY vs Jersey is hard to say:
http://www.infoq.com/news/2008/10/jaxrs-comparison
Regarding your error, you can control the content type via annotations, what happens if you place #Produces annotation, for example:
#Produces("application/json")
#GET
public Response getData() {
...
}
Well I know this requested is dated, and so much on the internet old..in a year of two everything usually changes and works better. So RestEasy should not get a bad rap in comparison to other non-propertary RESTLET frameworks.
Actually I think JBoss RestEasy has the lightest footprint, it's not bloated with unnecessary *.jars, flexible, fully certified JAX-RS implementation, complete and its ease of use is beyond comparison.
Some eluded, that a GET request should not expect a Content_Type on the request, (And I agree), but with a every GET request one must indicate what you intend on sending back to the requestor? Right! (will it be JSON, XML, plain text, XML and a sheetsheet, multi-part, etc). Well RestEasy, JBoss's framework addresses this with annotation as shown below, and configurable per URL REST request. Therefore, therein is your answer
#GET
#Path("/echo/{message}")
#Produces("text/plain")
public String echo(#PathParam("message")String message){
return message;
}
#GET
#Path("/employees")
#Produces("application/xml")
#Stylesheet(type="text/css", href="${basepath}foo.xsl")
public List<Employee> listEmployees(){
return new ArrayList<Employee>(employees.values());
}
#GET
#Path("/employee/{employeeid}")
#Produces("application/xml")
public Employee getEmployee(#PathParam("employeeid")String employeeId){
return employees.get(employeeId);
}
#GET
#Path("/json/employees/")
**#Produces("application/json")**
public List<Employee> listEmployeesJSON(){
return new ArrayList<Employee>(employees.values());
}
a GET request must not have a body, and an application must not expet a Content-Type header.
If this is a bug of RestEASY, it makes one wonder how many people really are using the software.
EDIT
RFC2616 $4.3
A message-body MUST NOT be included in
a request if the specification of the
request method (section 5.1.1) does
not allow sending an entity-body in
requests.
A server SHOULD read and forward a
message-body on any request; if the
request method does not include
defined semantics for an entity-body,
then the message-body SHOULD be
ignored when handling the request.
The GET method does not "does not allow sending an entity-body in request" therefore a GET request COULD have a body. But GET "does not include defined semantics for an entity-body" therefore the body should be ignored anyway.
In any case, RestEASY should not have required the presence of Content-Type in a GET request.

Categories

Resources