MTITEK.com
Spring Framework / Spring REST

Spring REST

Spring REST is built on Spring MVC: @RestController combines @Controller and @ResponseBody, causing every handler method to serialize its return value directly into the HTTP response body — no view resolution involved. spring-boot-starter-webmvc pulls in Jackson, which handles JSON serialization/deserialization automatically. The REST server in this project is a standalone Spring Boot web app; the REST client is a separate Spring Boot app running with WebApplicationType.NONE, using RestTemplate to drive HTTP calls against it.

@RestController

HTTP Method Mapping Annotations

@ResponseStatus

Maven Dependencies

<!-- REST server -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>

<!-- REST client: adds the above + the server artifact -->
<dependency>
    <groupId>com.mtitek.spring</groupId>
    <artifactId>mtitek-spring-rest</artifactId>
    <version>${project.version}</version>
</dependency>

The Model — AppProfile

@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
public class AppProfile {
    private String id;
    private String name;
    private Role role;

    public enum Role {
        USER, ADMIN, SUPPORT
    }
}

REST Controller — AppProfileController

@RestController
@RequestMapping(path = "/api/appProfiles", produces = "application/json")
public class AppProfileController {
    Map<String, AppProfile> appProfiles = new ConcurrentHashMap<>();

    @GetMapping()
    public Iterable<AppProfile> getAppProfiles() { ... }

    @GetMapping("/{id}")
    public ResponseEntity<AppProfile> appProfileById(@PathVariable("id") String id) { ... }

    @PostMapping(consumes = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<AppProfile> postAppProfile(@RequestBody AppProfile appProfile) { ... }

    @PutMapping(path = "/{id}")
    public ResponseEntity<AppProfile> putAppProfileById(@PathVariable("id") String id, @RequestBody AppProfile appProfileParam) { ... }

    @PutMapping(path = "/{id}/{role}")
    public ResponseEntity<AppProfile> putAppProfileByIdRole(@PathVariable("id") String id, @PathVariable("role") String role) { ... }

    @PatchMapping(path = "/{id}")
    public ResponseEntity<AppProfile> patchAppProfile(@PathVariable("id") String id, @RequestBody AppProfile patch) { ... }

    @DeleteMapping(path = "/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteAppProfile(@PathVariable("id") String id) { ... }
}

REST Client — AppProfileRestClient

// GET all — ParameterizedTypeReference required for generic collection
List<AppProfile> appProfiles = restTemplate.exchange(
    BASE_URL + BASE_PATH, HttpMethod.GET, null,
    new ParameterizedTypeReference<List<AppProfile>>() {}
).getBody();

// GET by id — body only vs. full ResponseEntity
AppProfile appProfile = restTemplate.getForObject(BASE_URL + BASE_PATH + "/{id}", AppProfile.class, 1);
ResponseEntity<AppProfile> responseEntity = restTemplate.getForEntity(BASE_URL + BASE_PATH + "/{id}", AppProfile.class, 1);

// PUT and DELETE — void return
restTemplate.put(BASE_URL + BASE_PATH + "/{id}", appProfile, appProfile.getId());
restTemplate.delete(BASE_URL + BASE_PATH + "/{id}", 3);