Reputation: 1827
I have one Java REST API which is used by 2 different consumers. By default REST principles my API should define the names of request headers. But now I have not common situation. Consumers use different security layer which provides different headers which means same parameter in both ways.
Example method: (not real code)
For 1st consumer:
@PostMapping("/number")
Integer getNumber(@RequestHeader("no") String number, @RequestBody User user) {
/*...*/
}
For 2nd consumer:
@PostMapping("/number")
Integer getNumber(@RequestHeader("number") String number, @RequestBody User user) {
/*...*/
}
I have up to 10 methods in one controller and they should be with same name and logic, but different header. The request path prefix could be different.
How to simplify REST controller and don't create 2 different controllers with same methods and same logic?
I tried several examples to create one controller with 2 different interfaces with same methods, but different mapping.
Controller class
@RestController
@RequestMapping(path ="/application")
@Api(tags = {"application"})
public class ApplicationController implements AppMapping1, AppMapping2 {
@Override
public Integer getNumber(String number, User user) {
/*...*/
}
}
First interface
interface AppMapping1 {
@PostMapping("/num")
Integer getNumber(@RequestHeader("num") String number, @RequestBody User user);
}
Second interface
interface AppMapping2 {
@PostMapping("/number")
Integer getNumber(@RequestHeader("number") String number, @RequestBody User user);
}
Controller maps only with the first interface. So
http://.../application/num
works fine, buthttp://.../application/number
- gets404
error code. That means Java Spring-Boot doesn't have such functionality. Need some more ideas.
Project developed with Java 8
; spring-boot:2.1.1.RELEASE
; gradle
Upvotes: 1
Views: 5823
Reputation: 1330
I found this answer on https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/spring-mvc-request-header.html
Avoid ambiguity by using @RequestMapping(headers = ....)
We can fix the ambiguity similar to @RequestParam where we used 'params' . In case of @RequestHeader we can define related headers in @RequestMapping annotation.
@Controller
@RequestMapping("trades")
public class TradesController {
@RequestMapping(headers = "User-Agent")
public String handleAllTradesRequests (@RequestHeader("User-Agent") String userAgent,
Model model) {
model.addAttribute("msg", "all trades requests, User-Agent header : "
+ userAgent);
return "my-page";
}
@RequestMapping(headers = "From")
public String handleRequestByFromHeader (@RequestHeader("From") String from,
Model model) {
model.addAttribute("msg", "trade request by From header : " + from);
return "my-page";
}
Upvotes: 1
Reputation: 1681
According to this , If we're not sure which headers will be present, or we need more of them than we want in our method's signature, we can use the @RequestHeader annotation without a specific name.
You have a few choices for variable type: a Map
, a MultiValueMap
or an HttpHeaders
object.
Sample
@PostMapping("/number")
public Integer getNumber(@RequestHeader Map<String, String> headers) {
if (Optional.ofNullable(headers.get("no")).isPresent()){
//...
}
else if (Optional.ofNullable(headers.get("number")).isPresent())
{
//...
}
}
Upvotes: 2
Reputation: 101
The best way is to add the HttpServletRequest
as an argument of your single controller and do some logic with the header map provided by the HttpServletRequest
object.
If you want to see a full example take a look here. I have implemented I single controller that wraps all my logic accordingly to headers/methods and so on. You can customize the logic as you want with the HttpServletRequest
.
Upvotes: 0
Reputation: 234
It is not maintenance friendly to repeat the same block of code twice or more times just to receive the same input with different names (number
and no
). Instead, it is advisable to read all the headers and traverse through it to fetch input using different names.
Sample Code
@PostMapping("/number")
public Integer getNumber(@RequestHeader Map<String, String> headers) {
String number = headers.containsKey("number") ? headers.get("number") : headers.get("no");
if(Objects.isNull(number)) {
throw new RuntimeException("Number input not received from header!");
}
// relevant processing
}
Upvotes: 1
Reputation: 297
You could remove the @RequestHeader
annotation and consider doing the following:
@PostMapping("/number")
Integer getNumber(HttpServletRequest request, @RequestBody User user) {
String number = request.getHeader("num");
if(number == null){
number = request.getHeader("number");
}
/*...*/
}
If you want a cleaner approach, consider creating a util class that takes the HttpServletRequest
object and returns the desired header value.
Upvotes: 0