How to use the same DTO with different roles in a RESTful API endpoint in Spring Boot?

I have a Spring Boot RESTful API with a PUT endpoint that can be accessed by different roles (ADMIN, MODERATOR, etc.). How to use the same DTO CreateOrUpdateRequest for all roles but with different field validations & serialization rules depending on the user’s role :

public class CreateOrUpdateRequest {
    @NotNull
    private String field1;

    @NotEmpty
    private String field2;

    @NotNull
    @ValidValueOfEnum(enumClass = MyEnum.class)
    private String field3;

}

For example, I want the ADMIN role to be able to update all the fields, but the MODERATOR can only update ‘field3’.

  1. Should I use a DTO per role ? If so, how can several DTOs be used for the same route and service method?

  2. I’ve tried the @JsonView approach but the unserialized fields will be null. My Mapstruct mapper will map null fields to null and automatically empty/delete fields :

@Mapper(componentModel = "spring")
public abstract class RequestMapper {
    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_NULL, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
    public abstract void update(CreateOrUpdateRequest request, @MappingTarget MyEntity entity);
}

Is @JsonView the right approach, or is there a better way to handle this in a Spring ?

10

Assume below service layer is accessed by an API, based on the user’s all the roles and permissions each field can be updated.

@Service
public class EntityService {

    @Autowired
    private EntityRepository entityRepository;

    @Autowired
    private UserService userService; // Service to get the currently logged-in user

    public void updateEntity(Long entityId, Map<String, Object> updates) {
        // Get the current user
        User currentUser = userService.getCurrentUser();
        Role userRole = currentUser.getRole();

        // Fetch the entity from the database
        Entity entity = entityRepository.findById(entityId)
                .orElseThrow(() -> new EntityNotFoundException("Entity not found"));

        // Perform field-level checks
        if (userRole == Role.USER_A) {
            if (updates.containsKey("field2")) {
                throw new AccessDeniedException("User A cannot update field2");
            }
            // Update field1
            entity.setField1((String) updates.get("field1"));
        } else if (userRole == Role.USER_B) {
            if (updates.containsKey("field1")) {
                throw new AccessDeniedException("User B cannot update field1");
            }
            // Update field2
            entity.setField2((String) updates.get("field2"));
        } else {
            throw new AccessDeniedException("Unknown role");
        }

        // Save the updated entity
        entityRepository.save(entity);
    }
}

It will be a single API but based on user’s all the permissions defined, validate the permission before updating each field and then allow the operation.

1

Going the route of multiple endpoints and multiple DTO classes, you could implement a @RequestMapping for each role and just pass that DTO and all required information to a common private method or a service method.

An example would be the following MyController.java with some additional classes, either within the same file or move to seperate files:

@Controller
@RequestMapping("/resources/{id}")
class MyController {

    private final Object adminMapper = null;
    private final Object managerMapper = null;

    @PostMapping("/admin")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<?> adminUpdate(@Valid @RequestBody AdminDto dto, BindingResult result) {
        return update(dto, result, adminMapper);
    }

    @PostMapping("/manager")
    @PreAuthorize("hasRole('MANAGER')")
    public ResponseEntity<?> managerUpdate(@Valid @RequestBody ManagerDto dto, BindingResult result) {
        return update(dto, result, managerMapper);
    }

    // alternativly the role instead of the mapper
    private ResponseEntity<?> update(UpdateDto dto, BindingResult result, Object mapper) {
        if (result.hasErrors()) {
            // TODO log and/or pass errors to response
            return ResponseEntity.badRequest().build();
        }

        // TODO implement rest / call service

        return ResponseEntity.noContent().build();
    }

}

enum MyEnum {
    FOO, BAR
}

// no default on fields present in both/all implementations necessary
sealed interface UpdateDto {
    default String field1() {
        return null;
    }

    default String field2() {
        return null;
    }

    MyEnum field3();
}

record AdminDto(
        @NotNull String field1,
        @NotEmpty String field2,
        @NotNull MyEnum field3) implements UpdateDto {
}

record ManagerDto(@NotNull MyEnum field3) implements UpdateDto {
}

In this example, the controller decides the mapper to use, but you could also just pass the DTO and the role to a service and let the service handle that instead. Obviously you do not have to use records or sealed interfaces. You could use an (not even necessarily) abstract class that contains field3 and have the admin DTO extend that class:

class UpdateDto {
    // TODO field3
}

class AdminUpdateDto extends UpdateDto {
    // TODO field 1
    // TODO field 2
}

Edit:
As noted, I’m not familiar with MapStruct, so you might have to call that mapper with the actual DTO and not the interface to make sure the null values from the default methods won’t be used.

An alternative would be to just have a Consumer as an argument that does mapping and remove the usage of an interface. Or just remove the private method alltogether and live with some code duplication (bindingResult handling, loading entity and mapping).

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật