I am working on an example project to understand better endpoint matchers::
#GetMapping(path ="/v3**", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<byte[]> genericV3GetRequestProxy(ProxyExchange<byte[]> proxy);
But when I send this http call it is not matching:
http://localhost:9000/api/v3asdasd/asd
Instead if I send this one it works:
http://localhost:9000/api/v3asdasd
In the end the correct solution is just:
#GetMapping(path ="/v3/**", produces = MediaType.APPLICATION_JSON_VALUE)
But I wanted to understand why it behaves like I showed.
When you added a slash to your URI you created a separate path segment, as / is defined as the path separator per RFC 3986.
https://www.rfc-editor.org/rfc/rfc3986#section-3.3
Related
I have URL #RequestMapping(value = {"/sessionkey"}, method = {RequestMethod.PUT}, produces = {"application/json"}) in my spring rest API. but it accepts special character, like "/sessionkey#?".
I want my API to accept only exact match the "/sessionkey" URL and should not accept any special character.
I'm working on a stubbing tool where a user can provide a list of endpoints and their associated JSON response. The idea is they will include a config file containing URIs and a directory of all stubbed responses. So essentially the config file will include the list of URI's that typically would be set using #RequestMapping if this was being built for a specific use case. Is there a way to have Spring send a success response even if the associated #RequestMapping doesn't exist?
Something similar to a SQL query e.g.
#RequestMapping(value = "/*", method = arrayOf(RequestMethod.POST))
fun sendResponse() : ResponseEntity<String> {
//Fetched correct response from interceptor
return ResponseEntity<String>
}
Found out the answer. You need to use #RequestMapping(value = "/**", method = arrayOf(RequestMethod.POST)).
After creating a resource in Spring REST Controller , I am returning it's location in header as below.
#RequestMapping(..., method = RequestMethod.POST)
public ResponseEntity<Void> createResource(..., UriComponentsBuilder ucb) {
...
URI locationUri = ucb.path("/the/resources/")
.path(someId)
.build()
.toUri();
return ResponseEntity.created(locationUri).build();
}
In Unit Test, I am checking its location as below.
#Test
public void testCreateResource(...) {
...
MockHttpServletRequestBuilder request = post("...")
.content(...)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
request.session(sessionMocked);
mvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("Location", "/the/resources" + id);
}
This result cases fails with following message.
java.lang.AssertionError: Response header Location expected:</the/resources/123456> but was:<http://localhost/the/resources/123456>
Seems like I have to provide context prefix http://localhost for Location header in expectation.
Is it safe to hard code context? If so, why?
If not, what is right way to generate it correctly for test case?
I'm guessing because you are using UriComponentsBuilder to build your URI it's setting the host name in your location header. Had you used something like just new URI("/the/resources"), your test would have passed.
In your case, I'd use redirectedUrlPattern to match the redirection URL:
.andExpect(redirectedUrlPattern("http://*/the/resources"))
This will match any hostname, so you don't have to hardcode localhost. Learn more about different patterns that you can use with AntPathMatcherhere.
If you don't need to have a full URI in the Location header on the response (i.e. no requirement, design constraint etc...): Consider switching to use a relative URI ( which is valid from HTTP standards perspective - see [1]: https://www.rfc-editor.org/rfc/rfc7231 ) Relative URIs is a proposed standard that is supported by modern browsers and libraries. This will allow you to test the behavior of the endpoint and make it less fragile in the long run.
If you need to assert the full path, since you are using MockMvc, you can set the uri in the test request to exactly what you want:
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void testCreateResource() {
MockMvc mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mvc.perform(MockMvcRequestBuilders.get(new URI("http://testserver/the/resources")));
This will make the injected builder produce "http://testserver" when build is called. Note of caution, a framework change in the future could cause you headaches if they remove this test behavior.
Facing the same problem, I tried out the solution Suraj Bajaj provided, and it worked fine for me.
I then took a try on asserting the text of the header field "Location" directly and ended up using a containsString().
This simple solution should also be a feasible alternative, as long as server context and the question of relative/absolute path are not of interest:
mvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("Location", containsString("/the/resources"+id)));
I'm trying to use Feign and Eureka to forward a post request from server A to server B. Both servers are discrovered sucessfully by Eureka.
This works:
#Feignclient
public interface MyFeignClient {
#RequestMapping(value = "test", = RequestMethod.POST, consumes = "application/json")
ResponseEntity<String> theActualMethod(
HttpServletRequest request,
#RequestHeader("firstHeader") String header1,
#RequestHeader("secondHeader") byte[] header2);
}
However, when I change the second argument to #RequestBody in order to read the POST request content, I get an exception:
java.lang.IllegalStateException: Method has too many Body parameters: public abstract org.springframework.http.ResponseEntity MyFeignClient.theActualMethod(javax.servlet.http.HttpServletRequest,java.lang.String,byte[])
The problem was that a method in Feign interface cannot have more than one 'general' argument. you can have as many header arguments as you want but not more than one as body. Since #RequestBody doesn't do anything it is regarded not as a header but as another variable in addition to the HttpServletRequest request variable.
So I had to change my business logic to have only one parameter.
For me, the issue was that I used #Param (as in feign.Param) instead of #RequestParam (as in org.springframework.web.bind.annotation.RequestParam). Changing all #Param to #RequestParam solved it for me.
I Don't know why this is but a related question on Feign's repository might explain a little.
I have a legacy system and some url rewrite rule that we want to get rid. One of which is a rule to change is /tools/lookup.html?what=this and change it to /tools/search?what=this and actually returns json and not html !
I'm trying to find a way to have a #Controller to support the legacy format lookup.html, but it fails with HTTP 406 "The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.". I'm wondering if anyone as done something similar?
My controller methods looks like:
#RequestMapping(value = "/tools/lookup.html", method = RequestMethod.GET)
public #ResponseBody Result lookup() {
return result;
}
Thanks in advance
Sylvain
Removing the reponsebody annotation will stop the controlle method returning json.
Take a closer look at #RequestMapping, which supports a produces element. E.g.,
#RequestMapping(value = "/tools/search", produces = "application/json")
#ResponseBody
public Result search(...) { ... }
The issue comes from how spring is treating pathvariables. The default behavior will cut off the last dot in a url (.html) to find the request mappings.
This effect happens only for the last pathvariable.
I havent found a property yet to change this globally but one way is to tell your pathvariable mapping to use the regex {pathvariable:.+}.
#Requestmapping("/somepath/{varwithextention:.+}")
public String method(#Pathvariable String varwithextension) {
...
}
Edit: i see that you do not even use pathvars. Probably its still the same effect for the last url part though?
Hi finally found something that should work in my case, note that my app doesn't need to support real html (REST only app), so this shouldn't have to much side effects. In my WebMvcConfigurerAdapter I added the following media type for html.
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("html",MediaType.APPLICATION_JSON);
configurer.mediaType("html",MediaType.APPLICATION_XML);
super.configureContentNegotiation(configurer);
}
I now I get my JSON or XML content back in the client. No more 406 error.