Using a String variable in RequestMapping value - java

I have the following:
#Value("${apiVersion")
private String apiVersion;
#RequestMapping(value = "/{apiVersion}/service/call", method = RequestMethod.POST)
And I expected the URL to be:
/apiVersion/service/call
But it turns out {foo} accept any value, it doesn't actually use the String.
Is there a way for me to use the String value as part of the URL?
EDIT
The issue is that I have multiple calls that us that value.
#RequestMapping(value = apiVersion + "/call1", method = RequestMethod.POST)
#RequestMapping(value = apiVersion + "/call2", method = RequestMethod.POST)
#RequestMapping(value = apiVersion + "/call3", method = RequestMethod.POST)
etc.
Technically I can declare constants for each one like you suggested, but it doesn't sound optimal. If there is no way to do it then it is fine, I was just wondering if there is.
SOLUTION
Adding general mapping to the controller.
#RequestMapping("${apiVersion}")

If you want to apply it for all methods in a controller declare it on the controller class level:
#RestController
#RequestMapping("/test")
public class MyController { ...
and you do not need to prepend it before method path.
Otherwise it should be constant so like:
private static final String FOO = "test";
and prepend it before method path like:
FOO + "/service/call"

If you just want to predefine the path in Java just do
#RequestMapping(value = foo + "/service/call", method = RequestMethod.POST)
PathVariables in SpringMvc are meant to be a placeholder for endpoints like in the following
#GetMapping(value = "/books/{id}")
public String displayBook(#PathVariable id) { ... }

Related

Java Optional query string paramters and Server side API's

Here is my requirement:
Step Five: Add searching by title
This method's purpose is to enable searching by title. You'll pass in an optional query string parameter that returns all auctions with the search term in the title.
In AuctionController.java, return to the list() action method. Add a String request parameter with the name title_like. You'll need to make this parameter optional, which means you set a default value for it in the parameter declaration. In this case, you want to set the default value to an empty string "".
Look in MemoryAuctionDao.java for a method that returns auctions that have titles containing a search term. Return that result in the controller method if title_like contains a value, otherwise return the full list like before.
My code that is NOT passing is this:
#RequestMapping(value = "title_like = ", method = RequestMethod.GET)
public List<Auction> searchByTitle(#RequestBody String title_like) {
if (!title_like.isEmpty()) {
for (Auction auction : auctions) {
if (dao.searchByTitle(title_like).contains(title_like)) {
auctions.add(auction);
return auctions;
}
}
}
return null;
}
}
Refactor the code like below
#RequestMapping(value = "search", method = RequestMethod.GET)
public ResponseEntity<List<Auction>> searchByTitle(#RequestParam(name="title_like", required=false) String title_like) {
...
return ResponseEntity.ok().body(<body>).build();
}
You are using GET method. With Get, it contain no body.
That why your signature don't work.
#RequestMapping(value = "title_like = ", method = RequestMethod.GET)
public List<Auction> searchByTitle(#RequestBody String title_like)
So you have to change #RequestBody to #RequestParam with GET method.
#RequestParam(name="title_like", required=false) String title_like
Other ways, you can change to different method to support body like (RequestMethod.POST, PUT...).
#RequestMapping(value = "title_like = ", method = RequestMethod.POST)
public List<Auction> searchByTitle(#RequestBody String title_like)

Spring RequestMapping for controllers that produce and consume JSON

With multiple Spring controllers that consume and produce application/json, my code is littered with long annotations like:
#RequestMapping(value = "/foo", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
Is there a way to produce a "composite/inherited/aggregated" annotation with default values for consumes and produces, such that I could instead write something like:
#JSONRequestMapping(value = "/foo", method = RequestMethod.POST)
How do we define something like #JSONRequestMapping above? Notice the value and method passed in just like in #RequestMapping, also good to be able to pass in consumes or produces if the default isn't suitable.
I need to control what I'm returning. I want the produces/consumes annotation-methods so that I get the appropriate Content-Type headers.
As of Spring 4.2.x, you can create custom mapping annotations, using #RequestMapping as a meta-annotation. So:
Is there a way to produce a "composite/inherited/aggregated"
annotation with default values for consumes and produces, such that I
could instead write something like:
#JSONRequestMapping(value = "/foo", method = RequestMethod.POST)
Yes, there is such a way. You can create a meta annotation like following:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#RequestMapping(consumes = "application/json", produces = "application/json")
public #interface JsonRequestMapping {
#AliasFor(annotation = RequestMapping.class, attribute = "value")
String[] value() default {};
#AliasFor(annotation = RequestMapping.class, attribute = "method")
RequestMethod[] method() default {};
#AliasFor(annotation = RequestMapping.class, attribute = "params")
String[] params() default {};
#AliasFor(annotation = RequestMapping.class, attribute = "headers")
String[] headers() default {};
#AliasFor(annotation = RequestMapping.class, attribute = "consumes")
String[] consumes() default {};
#AliasFor(annotation = RequestMapping.class, attribute = "produces")
String[] produces() default {};
}
Then you can use the default settings or even override them as you want:
#JsonRequestMapping(method = POST)
public String defaultSettings() {
return "Default settings";
}
#JsonRequestMapping(value = "/override", method = PUT, produces = "text/plain")
public String overrideSome(#RequestBody String json) {
return json;
}
You can read more about AliasFor in spring's javadoc and github wiki.
The simple answer to your question is that there is no Annotation-Inheritance in Java. However, there is a way to use the Spring annotations in a way that I think will help solve your problem.
#RequestMapping is supported at both the type level and at the method level.
When you put #RequestMapping at the type level, most of the attributes are 'inherited' for each method in that class. This is mentioned in the Spring reference documentation. Look at the api docs for details on how each attribute is handled when adding #RequestMapping to a type. I've summarized this for each attribute below:
name: Value at Type level is concatenated with value at method level using '#' as a separator.
value: Value at Type level is inherited by method.
path: Value at Type level is inherited by method.
method: Value at Type level is inherited by method.
params: Value at Type level is inherited by method.
headers: Value at Type level is inherited by method.
consumes: Value at Type level is overridden by method.
produces: Value at Type level is overridden by method.
Here is a brief example Controller that showcases how you could use this:
package com.example;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping(path = "/",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE,
method = {RequestMethod.GET, RequestMethod.POST})
public class JsonProducingEndpoint {
private FooService fooService;
#RequestMapping(path = "/foo", method = RequestMethod.POST)
public String postAFoo(#RequestBody ThisIsAFoo theFoo) {
fooService.saveTheFoo(theFoo);
return "http://myservice.com/foo/1";
}
#RequestMapping(path = "/foo/{id}", method = RequestMethod.GET)
public ThisIsAFoo getAFoo(#PathVariable String id) {
ThisIsAFoo foo = fooService.getAFoo(id);
return foo;
}
#RequestMapping(path = "/foo/{id}", produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public ThisIsAFooXML getAFooXml(#PathVariable String id) {
ThisIsAFooXML foo = fooService.getAFoo(id);
return foo;
}
}
You shouldn't need to configure the consumes or produces attribute at all. Spring will automatically serve JSON based on the following factors.
The accepts header of the request is application/json
#ResponseBody annotated method
Jackson library on classpath
You should also follow Wim's suggestion and define your controller with the #RestController annotation. This will save you from annotating each request method with #ResponseBody
Another benefit of this approach would be if a client wants XML instead of JSON, they would get it. They would just need to specify xml in the accepts header.
You can use the #RestController instead of #Controller annotation.
There are 2 annotations in Spring: #RequestBody and #ResponseBody. These annotations consumes, respectively produces JSONs. Some more info here.

Spring Rest Controller, Path Variables on an overriden method's arguement

I have a controller annotated with #RestController and it implements an interface:
public interface ContratEndpoint {
String ROOT = "/api/contrats";
String GET_CONTRAT = "";
String GET_CONTRAT_PER_PK = "/{idContrat}";
#RequestMapping(value = GET_CONTRAT)
Contrat getContrat(#RequestParam(value = "contratId")Long contratId);
#RequestMapping(value = GET_CONTRAT_PER_ID)
ExtContrat getContratById(#PathVariable("idContrat") Long idContrat);
}
The controller:
#RestController
#RequestMapping(value = ContratEndpoint.ROOT)
public class ContratController implements ContratEndpoint {
//Injecting Services....
#Resource
private Mapper mapper;
#Override
public Contrat getContrat(Long contratId) {
return mapper.map(contratService.get(contratId),Contrat.class);
}
#Override
public ExtContrat getContratById(#PathVariable("idContrat") Long idContrat){
Preconditions.checkArgument(idContrat !=null);
return mapper.map(contratService.get(idContrat),ExtContrat.class);
}
.The above Code works just fine.
. But For the first inherited method , I didn't have to annotate arguments with #RequestParam and it worked just fine.
As for the second method I tried at first :
#Override
public ExtContrat getContratById(Long idContrat){
Preconditions.checkArgument(idContrat !=null);
return mapper.map(contratService.get(idContrat),ExtContrat.class);
}
. I expected the same behaviour Like the first Method, But i was wrong and the code ended up firing an IllegalArgumentException because of the check in ligne Preconditions.checkArgument(idContrat!=null).
My question is what is so specific about #PathVariable that i've missed ?
Or is it just something is wrong with my approach?
Thanks.
There is difference between Request param and path variable,seee below post that you can confirm with your uri the cause for the exception :
#PathVariable is to obtain some placeholder from the uri (Spring call it an URI Template) — see Spring Reference Chapter 16.3.2.2 URI Template Patterns
#RequestParam is to obtain an parameter — see Spring Reference Chapter 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Assume this Url http://localhost:8080/SomeApp/user/1234/invoices?date=12-05-2013 (to get the invoices for user 1234 for today)
#RequestMapping(value="/user/{userId}/invoices", method = RequestMethod.GET)
public List<Invoice> listUsersInvoices(
#PathVariable("userId") int user,
#RequestParam(value = "date", required = false) Date dateOrNull) {
...
}

Cannot handle request by requestMapping

I need to handle requests like
www.example.com/student/thisisname?age=23&country=UK&city=London
I am just interested in thisisname part and value of city parameter.
I have following RequestMapping but it does not work. I tried {name}{.*:city} as well.
#RequestMapping(value = "/{name:.*}{city}", method = RequestMethod.GET)
You can do it by 2 ways. Either using #RequestParam or #PathVariable
By Using #RequestParam
#RequestMapping(value = "/name", method = RequestMethod.GET)
public void someMethod(#RequestParam String city){}
By using #PathVariable
#RequestMapping(value = "/name/{city}", method = RequestMethod.GET)
public void someMethod(#PathVariable String city){}
You can use any of this method just you need to concentrate on URL
You can handle it using PathVariable and RequestParam annotation. In below code name is thisisname part and city is query param city value.
#RequestMapping(value = "/student/{name}", method = RequestMethod.GET)
public void someMethod(#PathVariable String name, #RequestParam("city") String city){
}

Spring RequestMapping conflicts

I have a RequestMapping that displays a grid, and another one for loading objects in grid.
#RequestMapping(value = "/grid/{objType}", method = RequestMethod.GET)
public String displayGrid(Model model, #PathVariable("objType") String objType) {
// some code here
}
#RequestMapping(value = "/loadGrid", method = RequestMethod.GET)
public #ResponseBody String loadGrid(Model model) {
// returns a JSON
}
When i display the grid the url is like ../grid/User
The problem is that after the grid is created and a request loadGrid is made, the request is mapped to /grid/loadGrid which is resolved by the first method instead of the second one.
Is there any way to make a request for /grid with nothing after it ?
Or any way to resolve this conflict ?
The collision isn't a problem; spring resolves exact matches first. (see the source code of AbstractHandlerMethodMapping)
Your problem is that you've incorrectly defined your mappings. If you define a #RequestMapping at the class level, all the method #RequestMappings will be prefixed with the defined value.
The following maps three endpoints: /grid, /grid/{objType} and /grid/loadGrid. Note that the #RequestMapping for get() defines no value, only its method because it inherits from the class-level annotation.
#Controller
#RequestMapping(value = "/grid")
public class GridController {
#RequestMapping(method = RequestMethod.GET)
public String get(Model model) {
// ...
}
#RequestMapping(value = "/{objType}", method = RequestMethod.GET)
public String displayGrid(Model model, #PathVariable("objType") String objType) {
// ...
}
#ResponseBody
#RequestMapping(value = "/loadGrid", method = RequestMethod.GET)
public String loadGrid(Model model) {
// ...
}
}

Categories

Resources