Spring boot using Postman when making a POST with foreign key is returning null - spring

I am trying to make a post request using POSTMAN with Spring Boot
The relation between User and Role is (ManyToOne).
Why does role returns this: ("role":null)
POSTMAN VIEW:
{
"name": "usertest",
"lastname": "usertest",
"email": "usertest#gmail.com",
"role": {
"id": 1
}
}
POSTMAN OUTPUT:
{
"id": 29,
"name": "usertest",
"lastname": "usertest",
"email": "usertest#gmail.com",
"role": {
"id": 1,
"role": null
}
}
CONTROLLER:
#PostMapping("user")
public ResponseEntity<User> addUser(#RequestBody User user){
try {
userService.save(user);
HttpHeaders httpHeaders = new HttpHeaders();
return ResponseEntity.status(HttpStatus.CREATED)
.headers(httpHeaders)
.body(user);
}
catch (Exception e){
e.printStackTrace();
return null;
}
}
ENTITY USER:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, updatable = false)
private Role role;

You are mapping the input request body to a User object and persisting it into DB by calling userService.save(user) and you are NOT re-initializing user property with the persisted entity reference. So, it is a plain POJO, not a JPA managed entity. That's why the "role" property is still null.
You could return the persistent user from userService.save(user) method and return that from the Controller method. Also. you need to take care of loading Role inside User as it is a LAZY property.

Related

Why do I get random Http 404 from server between same requests with only one change in any field?

I haven an endpoint POST /api/marketplace/add that accepts a DTO object as request body. When I send the body below with platformName field set , server accepts request and processes it with no problem. But when I only try to change field platformName to null I get Http 404 error from server. I debugged the request and found out that it even can not reach controller method. I also got no trace from that error. What might be the cause that makes API respond differently to same request?
below
{
"platformName": "Trendyol",
"commissionAmounts": [
{
"amount": 23.45,
"categoryInfos": [
{
"categoryName": "Game"
}
],
"isCategoryBasedPricing": true
}
],
"shipmentAmounts": [
{
"amount": 23.45,
"scaleInfo": {
"order": 0,
"lowerBound": 0,
"upperBound": 0
},
"volumeInfo": {
"order": 0,
"lowerBound": 0,
"upperBound": 0
},
"isVolumeBasedPricing": true
}]
}
EDIT: dto model is
#Generated
public class MarketPlaceDTO {
#JsonProperty("platformName")
private String platformName;
#JsonProperty("commissionAmounts")
#Valid
private List<CommissionInfoDTO> commissionAmounts = new ArrayList<>();
#JsonProperty("shipmentAmounts")
#Valid
private List<ShipmentInfoDTO> shipmentAmounts = new ArrayList<>();
Controller is implementing swagger generated api interface. with postmapping and requestbody annotations.
#RequiredArgsConstructor
#RestController
public class MarketPlaceApiController implements MarketplaceApi {
private final MarketPlaceDAOService marketPlaceDAOService;
#Override
public ResponseEntity<BaseResponseDTO> addMarketPlace(MarketPlaceDTO
marketPlaceDTO) {
BaseResponseDTO dto =
marketPlaceDAOService.addMarketPlace(marketPlaceDTO);
return ResponseEntity.ok(dto);
}
}
Swagger generated api interface
#RequestMapping(
method = RequestMethod.POST,
value = "/marketplace/add",
produces = { "application/json", "application/xml" },
consumes = { "application/json" })
default ResponseEntity<BaseResponseDTO> _addMarketPlace(
#Parameter(name = "MarketPlaceDTO", description = "Add new
marketplace with given request body", required = true) #Valid
#RequestBody MarketPlaceDTO marketPlaceDTO) {
return addMarketPlace(marketPlaceDTO);
}
Response is
{
"timestamp": 1666866382906,
"status": 404,
"error": "Not Found",
"path": "/marketplace/add"
}
Obviously, that you use an endpoint with #RequestBody where body is a DTO.
And on trying to call this endpoint Spring Web first should match that a model in your request payload matches a require object in #RequestBody argument.
Ideally, using DTO as a request model is not a good idea. But I don't see your structure and cannot say if it's a problem or not.
The simple solution in your case is preparation (annotating) your DTO with specific JSON annotations:
#JsonInclude
#JsonIgnoreProperties(ignoreUnknown = true)
public class YourDTO {
private String platformName;
}
and for Controller add class annotation #Validated; for #RequestBody add #Valid annotation.
Recommendation: use request models for incoming objects, and later converters to DTO/entities with ability to response them with filtering (or in complex cases add also response model - usually it's overhead).
My problem was global exception handler component annotated with #ControllerAdvice. I tried to handle validation exceptions and forgot to add #ResponseBody to my handler methods which is in my case probabaly required. That somehow caused server to send http 404 message when any input validation exception was thrown. After I made changes , Exceptions was handled correctly by handler component.
#ControllerAdvice
#ResponseBody // this resolved my issue.
public class MVCExceptionHandler {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseErrorResponse
methodArgumentExceptions(MethodArgumentNotValidException e){
return BaseErrorResponse.builder()
.errorMessage(AppError.INVALID_OR_MISSING_USER_INPUT.getErrorMessage())
.errorCode(AppError.INVALID_OR_MISSING_USER_INPUT.getErrorCode())
.errorTime(Date.from(Instant.now())).build();
}

Spring Boot catch multiple exceptions and send as error response

I am validating an incoming POST request which will create a database entity after validating the request data. I am trying to gather multiple errors in a single request and respond as error response following JSON API spec:
https://jsonapi.org/examples/#error-objects-multiple-errors
HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
{
"errors": [
{
"status": "403",
"source": { "pointer": "/data/attributes/secretPowers" },
"detail": "Editing secret powers is not authorized on Sundays."
},
{
"status": "422",
"source": { "pointer": "/data/attributes/volume" },
"detail": "Volume does not, in fact, go to 11."
},
{
"status": "500",
"source": { "pointer": "/data/attributes/reputation" },
"title": "The backend responded with an error",
"detail": "Reputation service not responding after three requests."
}
]
}
Is it possible to do this by #ControllerAdvice. When Global exception handling is enabled by #ControllerAdvice and throws an exception, the next exception won't be caught.
Not directly, no. Not sure what is your business case/logic, therefore I don't know how you handling these exceptions in service layer, but in general, if you want to pass multiple errors in your #ExceptionHanlder - you could create a custom POJO:
public class MyError {
private String status;
private String source;
private String title;
private String detail;
getters/setters...
}
and then create a custom RuntimeException which would accept list of these POJOs:
public class MyRuntimeException extends RuntimeException {
private final List<MyError> errors;
public MyRuntimeException(List<MyError> errors) {
super();
this.errors = errors;
}
public List<MyError> getErrors() {
return errors;
}
}
And in your service layer you could create list of these POJOs, wrap then in your exception and throw it. Then in #ControllerAdvice you simply catch your exception and call accessor method to iterate against your list of POJOs to construct a payload you want.
Something like:
#ExceptionHandler (MyRuntimeException.class)
#ResponseStatus (BAD_REQUEST)
#ResponseBody
public Map<String, Object> handleMyRuntimeException(MyRuntimeException e) {
return singletonMap("errors", e.getErrors());
}

Spring Boot Inject Rest Template to Login Filter

in default, my Login Filter output like this :
"role": "ROLE_ABC",
"succeed": "Success!",
"id": 123,
"username": "111222333",
"token": "xxxxx"
now i want to inject the output of rest tempalte, the output of rest template like this one :
{
"ResponseHeader": {
"ErrorCode": "0",
"ErrorDescription": "Success",
"TrxId": "123"
},
"UserInfo": {
"UserId": "111222333",
"FullName": ""
}
}
now i want to inject the output of rest template to my default login filter.
this is my code, im confuse how i can send the Request Body of rest template inside Login Filter
#Autowired
RestTemplate restTemplate;
#Override
protected void successfulAuthentication(final HttpServletRequest req, final HttpServletResponse res, final FilterChain chain,
final Authentication auth) throws IOException, ServletException {
logger.info("successfulAuthentication");
logger.info(auth);
Set<String> roles = AuthorityUtils.authorityListToSet(auth.getAuthorities());
String hasil=roles.toString().replace("[","").replace("]", "");
AuthenticationService.addToken(res, auth.getName());
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Authorization", "Basic uYycjhow9iJOFOJj=");
HttpEntity<vwCredentials> entity = new HttpEntity<vwCredentials>(product,headers);
->variable "product" is request body,
i confuse where should i put this code "#RequestBody vwCredentials product"
ResponseEntity<vwJWTLDAP> respon = restTemplate.exchange(
"http://123/LDAP/", HttpMethod.POST, entity, vwJWTLDAP.class);
HashMap<String, Object> map = new HashMap<>();
String email = auth.getName();
User user = repository.findByEmail(email);
map.put("id", user.getId());
map.put("username", auth.getName());
map.put("role", hasil);
//map.put("LDAP", respon); -> I WANT TO CALL LIKE THIS WAY
map.put("token", AuthenticationService.addToken(auth.getName()));
map.put("succeed", "Success !");
String authString = new Gson().toJson(map);
PrintWriter out = res.getWriter();
res.setContentType("application/json");
res.setCharacterEncoding("UTF-8");
out.print(authString);
out.flush();
}
}
how can i call like this way. //map.put("LDAP", respon); -> I WANT TO CALL LIKE THIS WAY
lass.
You would go about this using the getBody() method. This method is inherited from the class HttpEntity.
That being said, a way to get it would be map.put("LDAP", respon.getBody());

Spring boot Authorization server redirection issue to client after successful authentication via Facebook

I am trying to setup a spring boot Authorizaiton server which will have the internal user login and OAuth2 with facebook. I am facing the below issues -
If I make my Authorization server SessionCreationPolicy.STATELESS then after successful authentication from facebook control get stuck in Authorization server itself (Its not returning to the my client application while if SessionCreationPolicy.IF_REQUIRED then control returns to my client app).
When I am using SessionCreationPolicy.IF_REQUIRED then control returns and I can do a authorization_code flow but the jwt token generated by spring-security-jwt gives me only user_name information in token which is facebook user's id (not even name).
My local user authentication code works fine with code flow and I can customize my token with custom token enhancer and add other properties also but when I try to customize facebook principal object to my custom user I get error that string can not be casted to custom user object.
Please refer to the repo for details - dev repo
I am using below code for setup/jwt generation -
#EnableOAuth2Client // for Oauth setup
// jwt enhancer which gives me error when principal is converted to custom user
class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
Authentication auth = authentication.getUserAuthentication();
/* additionalInfo.put("email", ((CustomPrincipal)auth.getPrincipal()).getEmail());
additionalInfo.put("roles", ((CustomPrincipal)auth.getPrincipal()).getRoles());
additionalInfo.put("id", ((CustomPrincipal)auth.getPrincipal()).getId());*/
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
//SSO filter i am using -
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(), client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
// filter.setAuthenticationSuccessHandler(authenticationHandler);
return filter;
}
Any help is appreciated.
Thanks!
I was able to get an explanation for 2nd and 3rd point-
Since after the authentication is successful from Facebook; Spring boot authorization server stores authentication object as below format -
{
"authorities": [
{
"authority": "ROLE_USER"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "xyzxyzxyzxyzxyz",
"tokenValue": "xyzxyzxyzxyzxyz",
"tokenType": "bearer",
"decodedDetails": null
},
"authenticated": true,
"userAuthentication": {
"authorities": [
{
"authority": "ROLE_USER"
}
],
"details": {
"id": "xyzxyzxyzxyzxyz",
"name": "xyzxyzxyzxyzxyz",
"email": "xyzxyzxyzxyzxyz"
},
"authenticated": true,
"principal": "xyzxyzxyzxyzxyz",
"credentials": "N/A",
"name": "xyzxyzxyzxyzxyz"
},
"principal": "xyzxyzxyzxyzxyz",
"oauth2Request": {
"clientId": "xyzxyzxyzxyzxyz",
"scope": [],
"requestParameters": {},
"resourceIds": [],
"authorities": [],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": null,
"refreshTokenRequest": null
},
so when I was casting my principal to custom principal I was getting the error since in above model principal is just a string.
Note - I still have no idea how i can customize the above authentication object to my customuser object.

How to display customized error response in REST API

My url is http://localhost:8090/employee/?emp_id=1551&name=
I am using Spring boot for designing REST application. I have used #RequestMapping and #RequestParam annotation for get resource. When I pass empty value to request parameter (for eg. name = ), I get below validation response(actual output section below).
However I wanted to override this output to display customized error response as below(expected section below).
How can I achieve this? How to avoid Spring's auto validation for input parameters in Get request?
Output
======
{
"timestamp": 1511144660708,
"status": 400,
"error": "Bad Request",
"message": "Required String parameter 'name' is not present",
"path": "/employee"
}
Expected
========
{
"errors":[
{
"id":"123144",
"detail": "invalid user input"
"status": "400"
}
]
}
Following sample code demonstrates how to customize error message for exception handling.
Create 2 POJOs for your customized response body.
Implement 1 method to catch the MissingServletRequestParameterException exception with #ExceptionHandler annotation for missing paramters.
Generate the response as you expected.
Class: ResponseProperty.java
public class ResponseProperty {
private int id;
private String detail;
private int status;
//getters and setters produced by IDE
}
Class: ResponsePOJO.java
public class ResponsePOJO {
List<ResponseProperty> errors;
public List<ResponseProperty> getErrors() {
return errors;
}
public void setErrors(List<ResponseProperty> errors) {
this.errors = errors;
}
}
Method: handleMethodArgumentTypeMismatch
#ExceptionHandler({ MissingServletRequestParameterException.class })
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(MissingServletRequestParameterException ex) {
ResponseProperty property = new ResponseProperty();
property.setId(123144);
property.setDetail("invalid user input");
property.setStatus(400);
ResponsePOJO responsePOJO = new ResponsePOJO();
List<ResponseProperty> propertyList = new ArrayList<ResponseProperty>();
propertyList.add(property);
responsePOJO.setErrors(propertyList);
return new ResponseEntity<Object>(responsePOJO, HttpStatus.BAD_REQUEST);
}
If you visit the endpoint /employee without required parameter, then you are going to see the response as follows:
Http Response
{
"errors": [
{
"id": 123144,
"detail": "invalid user input",
"status": 400
}
]
}
Hope this helps you! :)
UPDATE
If you want to get the request ID from header named requestId for response, you can use WebRequest to get this information as follows:
#ExceptionHandler({ MissingServletRequestParameterException.class })
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(MissingServletRequestParameterException ex,
WebRequest request) {
ResponseProperty property = new ResponseProperty();
property.setId(Integer.valueOf(request.getHeader("requestId")));
...
}

Resources