I have a Spring MVC web app. In it a form with a button that's supposed to delete a resource from another resource:
<td>
<form action="/product-bases/${productBase.id}/positions/${position.id}" method="DELETE">
<input type="submit" value="delete" />
</form>
</td>
My controller:
#Controller
#RequestMapping(value = "/product-bases/{id}/positions")
public class ProductBasePositionController {
#RequestMapping(value = "/{positionId}", method = RequestMethod.DELETE)
public ModelAndView delete(#PathVariable Integer productBaseId, #PathVariable Integer positionId) {
So in theory the server should route to the controller. But alas it does not, hence the post ;)
I'm getting
HTTP Status 405 - Request method 'GET' not supported
type Status report
message Request method 'GET' not supported
description The specified HTTP method is not allowed for the requested resource (Request method 'GET' not supported).
Apache Tomcat/7.0.19
Obviously I don't have a get for /positions/id defined yet, but why should I, I want to do a delete for now..
(I'm also trying to run this from my spring-test-mvc framework on a mock servlet without any tomcat implementation in between and it gives me a 400 - bad request..
)
So what am I missing here?
Oh, just to cut some corners: post and get will work for other resources, so the rest of my setup is fine.
The booting server even tells me:
RequestMappingHandlerMapping [INFO] Mapped "{[/product-bases/{id}/positions/{positionId}],methods=[DELETE],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView our.view.controller.ProductBasePositionController.delete(java.lang.Integer,java.lang.Integer)
Anyone as confused as I am? If less so, please enlighten me!
Forms can be submitted via GET or POST only (maybe also PUT, but I doubt that is widely implemented), as form submission requires a method where data is transmitted to the server.
The DELETE method does not have a request body, so specifying it in a form action is unsupported.
Do you have the HiddenHttpMethodFilter filter in your web.xml?
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
http://static.springsource.org/spring/docs/current/spring-framework-reference/html/view.html#rest-method-conversion
The error messages indicate that the browser is actually sending a GET request rather than a DELETE request.
What you need to do is:
examine the source of the web page that the browser is on at the time, and
using the browser's web debugger, see what the request URL and method actually are.
In Microsoft Azure portal i got this error for a Java App when i turned on Authentication/Authorization. If it's the same problem in your case, revert your action.
Related
I am trying to pass clusterId=1 as parameter from
<a href="http://192.168.11.134:8080/UniconnectConfigurationWeb/nodes?clusterId=1"> and get it into spring mvc controller through #PathParam("clusterId")Integer clusterId. But I'm getting 404 error.
Guide me how to pass parameter through anchor tag and how to hit controller and get the parameter value.
I am sharing my code below,
#RequestMapping(value = "/nodes?clusterId={clusterId}", method = RequestMethod.GET)
public ModelAndView nodes(#RequestParam("clusterId")Integer clusterId,HttpSession session, HttpServletRequest request) {
System.out.println(clusterId);
return dashboard;
}
}
<c:url var="myURL" value="http://192.168.11.134:8080/UniconnectConfigurationWeb/nodes">
<c:param name="clusterId" value="1"/>
</c:url>
Here you are using clusterId as Request Parameter , and passing from client side to server side. but in your server side code you are used ?clusterId={clusterId} in Request Mapping annotation and you are trying to receive that request parameter with #RequestParam Annotation. here #RequestParam is enough for receiving Request Parameter. so, no need to use this ?clusterId={clusterId}`, this is not correct way of writing server side URL.
it may helps you for better understanding #RequestParam vs #PathVariable
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.
I am adding REST API to my web application. All REST addresses are in ../rest/... What I want to do is return JSON : {error: not found} message when client try to invoke address which is not exist. For example :
../rest/users - OK
../rest/xxxx - return {error: not found} where xxxx can be any string.
Now when calling to ../rest/xxxx glassfish handle request and returns html error page.
Is there a way to disable glassfish from returning html error page for specific addresses (../rest/..) and to map all not existing urls to method which will return error formatted in JSON.
In applications that I have developed I have two hierarchy of pages; ones that return HTML (and HTML error messages) and ones that return JSON (and JSON error messages).
This technique may be of help here.
Can you not write a filter that intercepts all requests and return a json response for urls that do not follow the /rest/ url pattern ?
For example using the Jersey rest service, you would have a mapping like this
<servlet-mapping>
<servlet-name>Jersey Rest Servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You could add a Filter with a mapping
<filter-mapping>
<filter-name>RestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And then in the filter's doFilter method
public void doFilter(...){
if(match using a regex pattern){
chain.doFilter(request, response); //matches, send it to servlet
}else{
response.setContentType("application/json"); //no match, send an erro
response.println("{error:'value'}");
}
}
You may have to adjust the filter pattern to allow images and such stuff, but this gives you an idea to get started with.
I would like to my response of the Spring MVC Controller be seen as a static content in the browser. Just to fetch once and then get it from browser cache to get 304 Not Modified status code.
I have ordinary Spring MVC controller with simple method. simpleService.getVariables() is getting huge content from the database. It returns String.
#RequestMapping(value = "/jsContent.htm")
public ModelAndView jsContent(#RequestParam("ver") String version,
HttpServletRequest request, HttpServletResponse response) {
ModelAndView mav = new ModelAndView("jsContent");
mav.addObject("variables", simpleService.getVariables());
return mav;
}
Response is handling by Apache Tiles. The firstPart is ordinary JavaScript file, and the secondPart is the String added to the ModelAndView object.
<definition name="javascript" template="/WEB-INF/tiles/javascript.jsp" />
<definition name="jsContent" extends="javascript">
<put-attribute name="firstPart" expression="/js/content/static.js" />
<put-attribute name="secondPart" expression="${variables}" />
</definition>
To enable expressions in tiles I have added this tilesProperties:
This is my tiles/javascript.jsp:
<%# taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<tiles:insertAttribute name="firstPart" />
<tiles:insertAttribute name="secondPart" />
I have only achieved 304 Not Modified status code by added this Spring filter to the web.xml. However content is still fetching every time, and by Etag header comparison the status code is 304 when there is any change.
<filter>
<filter-name>etagFilter</filter-name>
<filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>etagFilter</filter-name>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>
How can I get cached response of my Spring Controller? I have tried to achieve this by HTTP Headers but it wasn't working.
The ETag method will not suit your need here. The way the filter works is that it calls the controller each time a request is received, it then computes the Hash of the response, compares the hash (ETag) with the Etag sent in the request, and if they match, the filter assumes data has not changed, and sends the client 304 with no response body.
As you can see, this method does not reduce the amount of work your actual controller needs to do for a request, it only improves bandwidth utilization and provides faster response to the client (Since instead of sending the response body, we are only sending a status code).
So, for your case, you should use the Last-Modified response header and If-Modified-Since request header. To do this with Spring MVC, you can try the following method:
1. In your controller, maintain a time stamp in millis, which should be the same as the last modified time for the resource. I set this to the deployment time, with the assumption that my content is not going to be modified while the application is live. You may chose to update this time as you wish, if you want to force browsers to load a fresh copy, even if they have a older version.
Accept org.springframework.web.context.request.WebRequest as a parameter to your controller method.
In your method, do the actual processing, only if webRequest.checkNotModified(lastModifiedTime) is false.
Sample code:
public class MyController {
//Injected
private long lastModifiedTime;
public void getFullSdk(WebRequest webRequest, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (webRequest.checkNotModified(lastModifiedTime)) {
return;
} else {
// real expensive code goes here
// write to response
}
}
}
Hope this helps.
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.