Home > Software design >  Handle Ambiguous handler methods mapped convention
Handle Ambiguous handler methods mapped convention

Time:11-28

I have 2 GET endpoints for Chemical resource: In the first endpoint, I want the chemical object by id, which is unique through every chemical.

@GetMapping("/chemical/{id}")

In the second GET endpoint, I want all the chemicals which are correspond to a specific lab (lab is mandatory).

@GetMapping("/chemical/{labKey}")

Spring cannot distinguish between /chemical/myLab and /chemical/12, I can understand this. I know I should change the endpoint mapping, but how? Can someone suggest a good convention for this specific case? eg. @GetMapping("/chemical/{labKey}/{id}" seems redundant for me, since I don't use labKey pathVariable, I will only need id, I will call something like this: chemicalService.findById(id).

CodePudding user response:

I think the best way to do this is by using two end points:

  1. /chemical/{id} which will fetch your chemical by identifier using @GetMapping("/chemicals/{id}").
  2. The second would use a query param labKey to filter based on the lab key. This will have a generic @GetMapping("/chemicals"). chemicals?labKey=lab1 would be the URI.

Below is my implementation. Controller class

import org.springframework.stereotype.Service;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ChemicalService {

    private List<ChemicalDTO> chemicals = Arrays.asList(
        new ChemicalDTO(1L, "chem1", "lab1"),
            new ChemicalDTO(2L, "chem2", "lab2"),
            new ChemicalDTO(3L, "chem3", "lab1"),
            new ChemicalDTO(4L, "chem4", "lab3")
    );

    public Optional<ChemicalDTO> getChemicalById(Long id) {
        return chemicals.stream().filter(e -> e.getId().equals(id)).findAny();
    }

    public List<ChemicalDTO> getChemicalByLabKey(String labKey) {
        if( labKey.isBlank() || labKey.isEmpty() ) return new ArrayList<>();
        return chemicals
                .stream()
                .filter(e -> e.getLabKey().equals(labKey))
                .collect(Collectors.toList());
    }

}

Service class

import org.springframework.stereotype.Service;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ChemicalService {

    private List<ChemicalDTO> chemicals = Arrays.asList(
        new ChemicalDTO(1L, "chem1", "lab1"),
            new ChemicalDTO(2L, "chem2", "lab2"),
            new ChemicalDTO(3L, "chem3", "lab1"),
            new ChemicalDTO(4L, "chem4", "lab3")
    );

    public Optional<ChemicalDTO> getChemicalById(Long id) {
        return chemicals.stream().filter(e -> e.getId().equals(id)).findAny();
    }

    public List<ChemicalDTO> getChemicalByLabKey(String labKey) {
        if( labKey.isBlank() || labKey.isEmpty() ) return new ArrayList<>();
        return chemicals
                .stream()
                .filter(e -> e.getLabKey().equals(labKey))
                .collect(Collectors.toList());
    }

}

DTO class

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class ChemicalDTO {

    private Long id;
    private String name;
    private String labKey;

}

You can find more information in the REST resource naming conventions and examples here

CodePudding user response:

If the path is the same and the number of path variables is also the same, below would be the clean approach to handle this situation.

you can consider below code snippet

@GetMapping(value = "/chemical", params = "id")
public String getChemicalById(@RequestParam(value = "id") String id) {
    return "ID method: " id;
}

@GetMapping(value = "/chemical", params = "label")
public String getChemicalByLabel(@RequestParam(value = "label") String label) {
    return "Label method: " label;
}

The URLs are as follows:

  • /chemical?id=ID
  • /chemical?label=LABEL
  • Related