Am working with Spring boot and I am using springdoc-openapi-ui to generate spec files using swagger editor
The issue Is, Am trying to avoid creating model classes just to use them in the request body to show them with swagger UI.
For example :
#RequestMapping(value = "/update/project/{id}", method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> projectUpdate(#RequestBody ObjectNode json, #PathVariable int id)
{ ... }
If I use it like this, the example will be empty on the Swagger UI page.
So as a solution, I have to do it like the following
public class CustomerCreateRequest {
#JsonProperty
private String ProjectId;
#JsonProperty
private String ProjectName;
#JsonProperty
private String ProjectDescription;
#JsonProperty
private String CustomerId;
#JsonProperty
private String CustomerName;
#JsonProperty
private String CustomerDescription;
#JsonProperty
private String BucketId;
#JsonProperty
private String API_KEY;
#JsonProperty
private String Name;
#JsonProperty
private String RedmineId;
And then I can use the model class I just created like the following.
#PostMapping(value = "/createUser")
public ResponseEntity createCustomer(#RequestBody CustomerCreateRequest requestBody)
{ ... }
Question
Is it ok to do a model class just for this purpose?
Is there a way to add an example so the UI team will have an idea of how to use it.
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Any Answer, Suggestion, Links are appreciated, the swagger docs was not helpful in my case.
my two cents:
Is it ok to do a model class just for this purpose?
Yes, you should use a model class for your #RequestBody becouse every endpoint must have a contract to communicate the payload necessary to be consumed.
It's a good practice add the annotations like
#Parameter(description="Some description", example="{\"foo\":\"bar\"}")
#RequestBody CustomerCreateRequest requestBody
Is there a way to add an example so the UI team will have an idea of how to use it.
No, Swagger will map a POJO class with decorators such as #Schema and others. ObjectNode has not a valid representation for the use case
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Well, in my experience use tools as Swagger have more benefits than cons. It's necessary take care about the constraints related? I think so
Related
Let me explain a problem. Suppose I have an entity class User:
public class User {
private UUID id;
private String login;
private String password;
private String firstName;
private String lastName;
private String email;
private int age;
// ... more fields and default getters and setters
}
In addition, I have two DTO classes:
public class UserLogin {
private UUID id;
private String login;
// ... getters and setters
}
public class UserLoginEmail {
private UUID id;
private String login;
private String email;
// ... getters and setters
}
Let's take a look to class UserController that has UserLoginEmail as request body and UserLogin as response body:
#RestController("/users")
public class UserController {
#PutMapping
public UserLogin someRequest(UserLoginEmail user) {
// ...
}
}
What is the best way to create some kind of projections in Spring Boot? Can I create an interface with required fields and just put them in the Java method as parameters (or some other way)? I want to build DTO classes with the least effort and agile in my code.
You could use JSON Views with Jackson with which you could define different views on a per endpoint basis (check https://www.baeldung.com/jackson-json-view-annotation for more details).
But in your case, I wouldn't do that. One of your DTOs is a request and the other is a response so you shouldn't mix them together in a single DTO. Even more than that, I don't really like JSON Views because they are simply hard to follow and the code becomes harder to read. Abstractions and code reusability are usually good but it makes the code much harder to read and for the case of DTOs I much more prefer to be explicit and have multiple DTOs even that they are similar. With this approach, you will make it possible to easily change one of the DTOs without affecting anything else, which is not the case when you reuse them in any way.
Having said that, keep both DTOs, but I would rename them: UserLoginRequest and UserLoginResponse.
I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.
I have one REST API which is using a POST Call to create a record, I'm expecting certain Object to be passed in post call, if anything is missing i have to reject straight away from their only,
#RequestMapping(value="/saveEssentialDetails",produces={"application/json"},method=RequestMethod.POST)
ResponseEntity<?> saveEssentialDetails(#ApiParam(value="Body Parameters")#RequestBody #Validated EssentialDetails essentialDetails, BindingResult bindingResult)throws Exception;
and the Essential Model class is as follow
#Data
#NoArgsConstructor
#Document(collection="essentialDetails")
public class EssentialDetails {
#NotNull
Integer dpId;
#Id
#NotEmpty
String tpId;
#NotEmpty
List<FamousFor> famousFor;
#NotEmpty
List<OpenHours> openHours;
#NotEmpty
Pictures uploadedImages;
#NotEmpty
List<FloorDescription> floorDescriptions;
#NotEmpty
List<Outlets> mallOutlets;
}
But while making a Post Call with Missing attributes i'm allowed to make an entry in MongoDB, which i don't want to persist as it's not a proper request,#Validation is not working for me, i'm using spring boot 2.0.6 with MongoDb 4.0.4,
any help would be highly appreciated. Thanks well in advance
#Validated can be used to validate a object with a custom validation object. Example usage:
#RequestMapping(value = "/")
public String request(#Validated(Account.ValidationStepOne.class) Account account)
Instead of using #Validated use #Valid which does check for the validation annotations that you are using in your entity.
I've the following domain and needs to return selected field in response to client. How can I achieve that using Spring?
public class Vehicle {
private String vehicleId;
private Long dateCreated;
private String ownerId;
private String colourCode;
private String engineNumber;
private String transmission;
//getters & setters
}
My objective is to return only colourCode and transmission fields to client request. I've read about DTO and seems like I can achieve my objective with DTO but I don't find any good example how to implement it. Is DTO is the correct way to achieve my objective ?
Basically you just create VehicleDTO class with parameters you need
public class VehicleDTO {
private String colourCode;
private String transmission;
//getters and setters
}
and then in your code you construct VehicleDTO from your Vehicle class. Fortunately, we have BeansUtils class from Spring, that uses reflection to copy properties of one object to another, because you do not want to repeat logic for copying properties for every object. So it would be something like:
BeanUtils.copyProperties(v1, dto);
At the end your return VehicleDTO in your response instead of Vehicle
You can return IVehicle interface which exposes your properties of choice
public interface IVehicle {
String getTransmission();
String getColourCode();
}
and your Vehicle implents it
public class Vehicle implements IVehicle{ }
There are various ways you can achieve what you want.
You can add relevant usecase / APi specific DTO for the resource.
e.g. If your API return the vehical general details you may want to expose some level of details,
public class VehicleDetailsDTO {
private String colourCode;
private String transmission;
private String engineNumber; //more
//getters and setters
}
You can then either use BeanUtils or Dozzer to convert your Vehical resource to transportable object like your DTO.
BeanUtils : http://commons.apache.org/proper/commons-beanutils/
Dozzer : http://dozer.sourceforge.net/documentation/mappings.html
Assuming you use JSON as output format and Jackson as serialization engine (default in Spring MVC), you can tell Jackson to not serialize null properties. Now you just need to populate the properties you need and can return the original business object.
Below is my application code
#RequestMapping(value = "app/{version}/register", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public RegisterMemberResponse registerMember(#Valid #RequestBody RegisterMemberRequest request,#PathVariable Integer version){
return registerservice.register(request);
}
for app version 1 my RegisterMemberRequest class is
public class RegisterMemberRequest{
#NotNull
private String firstName;
#NotNull
private String lastName;
//gatter & setter...
}
and for app version 2 my request class is
public class LatestRegisterMemberRequest extends RegisterMemberRequest{
#NotNull
private String middleName;
//getter & setter...
}
so how i can i change my registerMember() method in such a way that i can serve both version 1 & 2 request uri.
If you want to hardcode the version number into the url (which I would not recommend), you could easily use two different controller methods with hardcoded paths like app/v1/register and app/v2/register.
Another way would be to use content negotiation and route the requests according to a given content-type, e.g. application/vnd+<YOURCOMPANY>.<DATATYPE_AND_VERSION>+json, also using separate rest controller methods annotated with #Consumes.
I think you can make use of Requestmapping-uri-templates-regex , version number will be the #PathVariable write your business logic to decide what should be the response body depends on pathvariable version.