recently we moved to spring 3.0 Controller handling like this:
#Controller
public class MyController {
#RequestMapping(method = RequestMethod.POST)
protected String onSubmit ( Form form, Errors errors) {
// handle POST
}
#RequestMapping(method = RequestMethod.GET)
protected void getForm ( Form form ) {
// handle GET
}
}
Now we are getting lots of Exceptions in our logs because of HEAD Requests.
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'HEAD' not supported
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.resolveHandlerMethod(AnnotationMethodHandlerAdapter.java:621)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:422)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:415)
...
I would like to support HEAD Requests the same way like GET Requests, but obeying the HTTP reference of course:
The HEAD method is identical to GET
except that the server MUST NOT
return a message-body in the response.
The metainformation contained in
the HTTP headers in response to a HEAD
request SHOULD be identical to the
information sent in response to a GET
request. This method can be used
for obtaining metainformation about
the entity implied by the request
without transferring the entity-body
itself. This method is often used
for testing hypertext links for
validity, accessibility, and recent
modification.
http://www.ietf.org/rfc/rfc2616.txt
Does anybody has an elegant solution or is there even a spring solution out-of-the-box?
I searched the web but did not find any answers to this.
I believe this is what you're looking for:
http://www.axelfontaine.com/2009/09/transparently-supporting-http-head.html
In the current Spring (4.3.10) HEAD is automatically supported:
#RequestMapping methods mapped to "GET" are also implicitly mapped to
"HEAD", i.e. there is no need to have "HEAD" explicitly declared. An
HTTP HEAD request is processed as if it were an HTTP GET except
instead of writing the body only the number of bytes are counted and
the "Content-Length" header set.
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestmapping-head-options
Just add HEAD as a supported method the the request mapping:
#RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD})
Update: I think you can provide a custom class that extends AnnotationMethodHandlerAdapter to be the method handler (in dispatcher-servlet.xml), and just bypass the HEAD support check there. But I'd just use the replace features of an IDE to add it.
Related
Is there a way to get the registered URL pattern (In Controller) from the incoming HTTP request.
For Example, If my controller code is
#RestController
#RequestMapping("greeting")
public class Greeting {
#GetMapping("/{text}")
public String echo(#PathVariable("text") String text) {
return text;
}
}
Incoming request is http://localhost:8080/greeting/Hello,
I want to get the mapping value /greeting/{text}. Can I achieve the same using any Interceptor or Handler?
I have registered all the original mapping URLs like /greeting/{text}, /users/find/{id} etc., and want to perform some validation if the incoming request falls into any of those registered requests. I could have achieve the same using one custom method level annotation but I don't want to change all the codes now. It would be nice if I can achieve the same from a single Interceptor or extending any spring handlers.
The above snippet is a sample one. I have 4 to 5 spring applications with different apis and want create a common security package which can achieve the same.
You can access this from
final String pattern = request.getAttribute(org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE):
Source: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerMapping.html#BEST_MATCHING_PATTERN_ATTRIBUTE
HttpServletRequest request
request.getCurrentUrl() can get the url
We plan to use content type negitiation as a form of versioning for our rest API but it seems more complex than it should be.
Given the follwing example code :
#RestController
#RequestMapping("/products")
ProductController {
.......
#RequestMapping(value = "/{productID}", method = RequestMethod.GET, produces = "productVersion2/json")
public ResponseEntity<ProductV2> getVersion2(#PathVariable String productID) {
.......
return new ResponseEntity<ProductV2>(product, HttpStatus.OK);
The correct method is being called when test this from e.g postman, but i get a HTTP 406 Not Acceptable as a response. I have been looking several places, but I have not found a a page with a good explanation of what i need to do to make this work.
The response is to be parsed with json just like all other requests, but the response object is modified.
The thought is that we by doing this can support several "versions" of the same API and we can gradually make clients move over to the new api, while still having the same uri/resource to access.
Can anyone point to a good tutirial or a step by step guide of how this can be solved in spring boot ?
I have read this article : http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc/#combined-controller
but I was not able to get a clear anderstanding of what was needed to make it work
Your media type is wrong. To use custom media types for API versioning you could use application/productVersion2+json instead of productVersion2/json. I suspect you get the 406 because Spring Boot has no way to find out how to serialize your object into JSON because productVersion2/json isn't a valid media type for json data.
There is more than one way to pick a media type to do this, I've googled a more comprehensive document here.
Is there possible get request type in controller? How?
To detect if the request is a master or not requires the use of the RequestStack, which should be injected into your controller. The request stack has 3 useful methods
getCurrentRequest();
getMasterRequest();
getParentRequest();
The getParentRequest() will always return null if the current request is the master.
I was looking for this myself, and it seems it is just passed around, so there doesn't seem to be one single place that knows what it is.
My thought for solving this would be to create a simple kernel.request listener that just adds an attribute to the request. Rough (un-tested) code below:
public function onKernelRequest(GetResponseEvent $event)
{
$event->getRequest()->attributes->set('_request_type', $event->getRequestType());
}
Then in the controller you should be able to do:
$requestType = $this->getRequest()->attributes->get('_request_type');
Again this is untested. You would need to write out the full listener class and add it to the services config file, but other than that I think this will work.
Easy, just call the getMethod() method on your Request object:
$method = $this->get('request')->getMethod();
This will return the HTTP method of the current request, e.g. GET, POST, PUT or DELETE.
I have a web service (an ASP.NET .asmx page), and for debugging purposes I need to log all calls to the webservice, including values of all parameters passed into each call. So basically the first thing each WebMethod should do is log it's state with details of all the parameter values passed in to it.
So far so good. The complication is that I also want an automated way of getting the parameter values - there's quite a few webmethods with different signatures, and some of them have up to ~30 parameters, so manually coding against each specific parameter would likely be massively error-prone. I'd rather be able to call a method that looks at the current Http context and automatically uses that to grab and parse whatever has been passed in by the client.
But I hit a snag. When I look at HttpContext.Current.Request, it turns out that both the Form and QueryString collections are empty. So if the arguments passed to the webmethod aren't in either of those collections, where would they be? Anyone know how I can retrieve them?
You can use AOP techniques for this task. Considering PostSharp, you can create custom aspect like this:
[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Trace.WriteLine(string.Format("Entering {0}", args.Method.Name));
for (int i = 0; i < args.Arguments.Count; i++)
{
Trace.WriteLine(string.Format(" {0}", args.Arguments.GetArgument(i)));
}
}
}
and then apply it to your web-service methods:
[WebMethod, Trace]
public string HelloWorld()
{
return "Hello World";
}
You could use SOAP extensions and follow the example in this post to log the request which would have the method name and parameters.
SOAP Extentions is a better choice. Here is another example to retreive SOAP request and SOAP response as XML. All you do is parse the XML to retreive parameter name value pairs.
I have a small question regarding Spring's MVC data binding capabilities.
I do have the following controller class:
#Controller
#RequestMapping("/foo")
public class FooController() {
// … some init stuff //
#RequestMapping(value = "/{id}/edit.{format}", method = RequestMethod.POST)
public ModelAndView editFoo(#RequestBody FooItem foo, #PathVariable("format") String format) {
// some code here to edit the FooItem //
}
}
I want to be able to post form data as well as XML against this method. For that to work I added two message converters to my applicationContext.xml: The default formHttpMessageConverter and an XStream marshaller.
This works fine, but I have a problem, that if I use #RequestBody and post form data against the URL, the server responds with a 415 Error. If I remove this annotation, form data works well and Spring creates the object for me, but if I post XML against it, I get an empty object.
Is there any way around this or do I need to have 2 methods to be able to handle both of the incoming formats?
Thanks in advance!
I think you need two methods.
FormHttpMessageConverter doesn't have the same databinding capabilities as #ModelAttribute provides, it can't bind request to the specified target class, only to MultiValueMap (see javadoc).