please i have this error when trying to create a customer. Can some one help me? May be i am missing some thing. I have even try to change the #PostMapping to #RequestMapping till yet. Thks
My Controller code
`#PostMapping("CREATE_CUSTOMER_ENDPOINT")
#ResponseStatus(value = HttpStatus.OK)
#ApiResponses(value = {
#ApiResponse(code = 201, message = "The Customer was Created", response = CustomerDto.class),
#ApiResponse(code = 400, message = "Bad Request", response = ResponseError.class),
#ApiResponse(code = 500, message = "Unexpected error")
})
public ResponseEntity createCustomer(final HttpServletRequest request, #RequestBody CustomerDto customerDto)
{
if (log.isDebugEnabled()){
log.debug("[CustomerResource] POST {} : Creating customer ", CREATE_CUSTOMER_ENDPOINT);
}
if(customerDto.getUidpk()!=null) {
ResponseError error = new ResponseError(HttpStatus.BAD_REQUEST.getReasonPhrase(), "A customer Already exist with an Uidpk");
log.error("[CustomerResource] The customer Already exist ({}) with an Uidpk", customerDto.getUidpk());
return new ResponseEntity<>(error, null, HttpStatus.BAD_REQUEST);
}
CustomerDto result = customerService.createCustomer(customerDto);
log.debug("[CustomerResource] Customer created ({})", result.getUidpk());
return new ResponseEntity<>(result, HeaderUtil.putLocationHeader(request.getRequestURL().toString() + "/" + result.getUidpk()), HttpStatus.CREATED);
} `
My endpoints
private static final String CUSTOMER_SEARCH_USER_ID_ENDPOINT = "/customers/{userId:.+}";
private static final String CREATE_CUSTOMER_ENDPOINT= "/customer";
private static final String UPDATE_CUSTOMER_ENDPOINT= "/customer";
private static final String DELETE_CUSTOMER_ENDPOINT = CREATE_CUSTOMER_ENDPOINT + "/{uidpk}";
This is the response of Postman
Postman sample
When you send JSON payloads in HTTP request, you need to specify Content-Type HTTP header with value application/json.
Related
As shown in the picture, I want to add my description in my Swagger UI. The value "Example" (in name and description) comes from the parameter "Example" in the controller where I have used the #Requestbody. I am using Spring framework. How do I add my description to it?
This is my controller file
#ApiOperation(value = "It will be used to print the document of quotation.",response = GenerateDocPrintResponse.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully retrieved list"),
#ApiResponse(code = 401, message = "You are not authorized to view the resource"),
#ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found")
}
)
#RequestMapping(value = "/service/generateDocPrint", method = RequestMethod.POST)
public #ResponseBody String generateDocPrint(#RequestBody GetdocprintRequest Example,HttpServletRequest request, HttpServletResponse response,
#RequestBody String newJson) throws Exception {
................
}
This is my Pojo file
package com.iii.fw.models.generatedocprint;
import com.iii.fw.models.common.RequestHeader;
import io.swagger.annotations.ApiModelProperty;
public class GetdocprintRequest {
RequestHeader reqHeader;
private ReqPayloadGDP reqPayload;
public ReqPayloadGDP getReqPayload() {
return reqPayload;
}
public void setReqPayload(ReqPayloadGDP reqPayload) {
this.reqPayload = reqPayload;
}
public GetdocprintRequest withReqPayload(ReqPayloadGDP reqPayload) {
this.reqPayload = reqPayload;
return this;
}
}
#ApiOperation(value = "It will be used to print the document of quotation. ", notes = "put your description here ", response = )
Does this answer your question ?
Use the notes proptery.
Using notes, we can provide more details about the operation. For instance, we can place a text describing the endpoint's restrictions:
https://www.baeldung.com/swagger-apioperation-vs-apiresponse#2-the-notes-property
#ApiOperation(value = "Gets customer by ID", notes = "Customer must exist")
I'm using Spring Boot 2.6.7 and Using Open API springdoc-openapi-ui 1.6.4. I have 2 services. From first service I'm using rest template to connect to second service.
In the first service, in rest controller api, I have used HttpEntity to get request object. The same is passed to rest template. The reason is with HttpEntity, I'm passing the request body as well as some other headers as well.
My controller method is as follows.
#PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
#Operation(summary = "API for submit", description = "Submit data")
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "OK"),
#ApiResponse(responseCode = "400", description = "Bad request", content = #Content(schema = #Schema(implementation = Failure.class))),
#ApiResponse(responseCode = "500", description = "Error", content = #Content(schema = #Schema(implementation = Failure.class))), })
public ResponseEntity<Success<SubmitOpr>> submit(HttpEntity<OperationReq> httpEntity) throws Exception {
log.info("Request Entity is {}", httpEntity);
log.info("Request Body is {}", httpEntity.getBody());
SuccessResponse<SubmitOpr> response = null;
try {
response = oprService.submit(httpEntity);
} catch (Exception e) {
log.error("Failure: {}", e.getMessage());
throw e;
}
return ResponseEntity.ok().body(response);
}
My application works fine with this. And with postman client also it works fine.
But when I use swagger UI to test, I did not get expected result. And when I debug, httpEntity.getBody() is null
If I change from HttpEntity<OperationReq> httpEntity to OperationReq httpEntity and then accordingly change subsequent service layer methods, the api works fine in swagger.
But I don't want to change that. Because I want to pass HttpEntity and another thing is there are so many similar APIs and it would be very difficult to change everywhere.
Is there a better solution to this?
I think there is no direct solution to this. If you are looking to get headers along with request body, what you can do is, you can get RequestBody and get Headers in controller and create an HttpEntity object. And that you can pass on to your service layer methods.
Change will be only in controller side and no change will be required from service layers.
#PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
#Operation(summary = "API for submit", description = "Submit data")
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "OK"),
#ApiResponse(responseCode = "400", description = "Bad request", content = #Content(schema = #Schema(implementation = Failure.class))),
#ApiResponse(responseCode = "500", description = "Error", content = #Content(schema = #Schema(implementation = Failure.class))), })
public ResponseEntity<Success<SubmitOpr>> submit(#RequestBody OperationReq operationReq, #RequestHeader MultiValueMap<String, String> headers) throws Exception {
HttpEntity<OperationReq> httpEntity = new HttpEntity<>(operationReq, headers);
log.info("Request Entity is {}", httpEntity);
log.info("Request Body is {}", httpEntity.getBody());
SuccessResponse<SubmitOpr> response = null;
try {
response = oprService.submit(httpEntity);
} catch (Exception e) {
log.error("Failure: {}", e.getMessage());
throw e;
}
return ResponseEntity.ok().body(response);
}
I had the same behavior of Swagger for HttpEntity controller method parameter. In my case the problem was that Swagger sent httpEntity as a query parameter instead body.
I have added a #io.swagger.v3.oas.annotations.parameters.RequestBody annotation and it solved the problem:
#io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
description = "Put here your feature configuration DTO in a JSON format",
content = #Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = #ExampleObject(value = "{}")
)
)
HttpEntity<String> httpEntity
I am calling a POST service getOrder3 written in SpringBoot which is working fine (tested in Postman), but getting error when called via restTemplate.postForObject from another service. I tried 2 versions of the client service getOrderClient and getOrderClient2, but both are giving same error :
HttpClientErrorException$BadRequest: 400 : [no body]
Please find the details below. Any help is appreciated.
getOrder3
#PostMapping(value="/getOrder3/{month}",produces="application/json")
public ResponseEntity<OrderResponse> getOrder3(
#PathVariable("month") String month,
#RequestParam String parmRequestSource,
#RequestParam(required=false) String parmAudienceType,
#RequestBody OrderRequestForm orderRequestForm) {
OrderResponse orderResponse = new OrderResponse();
log.info("In getOrder3...parmRequestSource = " + parmRequestSource + " parmAudienceType = " + parmAudienceType);
try {
//validate JSON schema
//orderService.validateMessageAgainstJSONSchema(orderRequestForm);
//process order
orderResponse = orderService.processOrder(orderRequestForm);
orderResponse.setParmRequestSource(parmRequestSource);
orderResponse.setParmAudienceType(parmAudienceType);
orderResponse.setMonth(month);
}catch (Exception e) {
throw new OrderException("101", e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(orderResponse,HttpStatus.OK);
}
The service is working fine , tested in postman
Now when I try to call via another microservice via restTemplate.postForObject, I get the error. Tried 2 versions of the client as below, getOrderClient and getOrderClient2
getOrderClient
#PostMapping(value="/getOrderClient/{month}",produces="application/json")
public OrderResponse getOrderClient(
#PathVariable("month") String month,
#RequestParam String parmRequestSource,
#RequestParam String parmAudienceType,
#RequestBody OrderRequestForm orderRequestForm) throws URISyntaxException, JsonProcessingException {
RestTemplate restTemplate = new RestTemplate();
URI uri = new URI("http://localhost:51001/orders/v1/getOrder/"+month+"?parmRequestSource="+parmRequestSource+"&parmAudienceType="+parmAudienceType);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String requestJson = new ObjectMapper().writeValueAsString(orderRequestForm);
HttpEntity<String> httpEntity = new HttpEntity<String>(requestJson,headers);
String response = restTemplate.postForObject(uri, httpEntity, String.class);
return new ObjectMapper().readValue(response, OrderResponse.class);
}
getOrderClient2
#PostMapping(value="/getOrderClient2/{month}",produces="application/json")
public OrderResponse getOrderClient2(
#PathVariable("month") String month,
#RequestParam String parmRequestSource,
#RequestParam String parmAudienceType,
#RequestBody OrderRequestForm orderRequestForm) throws URISyntaxException, JsonProcessingException {
RestTemplate restTemplate = new RestTemplate();
URI uri = new URI("http://localhost:51001/orders/v1/getOrder/"+month+"?parmRequestSource="+parmRequestSource+"&parmAudienceType="+parmAudienceType);
return restTemplate.postForObject(uri, orderRequestForm, OrderResponse.class);
}
Both are giving same error :
HttpClientErrorException$BadRequest: 400 : [no body]
Please suggest.
To improve the visibility of the solution, #astar fixed the issue by annotating the model object's properties with #JsonProperty.
I instatiate docket like this
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.config.internal"))
.paths(Predicates.or(PathSelectors.ant("/api**/**")))
.build();
}
I created a set of stub endpoints that imitate the real one for /login or /oauth.
#Api("Authentication")
#RequestMapping("/api")
public interface LoginEndpointApi {
#ApiOperation(value = "Github SSO endpoint", notes = "Endpoint for Github SSO authentication")
#ApiResponses({
#ApiResponse(code = 200, message = "HTML page of main application")
})
#GetMapping("/oauth/github")
default void oauthGithub() {
throw new UnsupportedOperationException();
}
#ApiOperation(value = "Get CSRF token", notes = "Returns current CSRF token")
#ApiResponses({
#ApiResponse(code = 200, message = "CSRF token response", response = String.class,
examples = #Example({#ExampleProperty(value = "015275eb-293d-4ce9-ba07-ff5e1c348092")}))
})
#GetMapping("/csrf-token")
default void csrfToken() {
throw new UnsupportedOperationException();
}
#ApiOperation(value = "Login endpoint", notes = "Login endpoint for authorization")
#ApiResponses({
#ApiResponse(code = 200, message = "Successful authentication")
})
#PostMapping("/login")
default void login(
#ApiParam(required = true, name = "login", value = "login body")
#RequestBody LoginRequest loginRequest) {
throw new UnsupportedOperationException();
}
}
But it doesn't recognize it. It is located in the same com.config.internal package as I described.
But the page swagger ui is empty and shows that No operations defined in spec!
What is the problem?
If you want to provide swagger documentation for your request mappings specified above you could simply describe it with .paths(Predicates.or(PathSelectors.ant("/api/**"))) path matchers. But if your path includes something more complicated like api + text without backslash separator then you should get known with
https://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/util/AntPathMatcher.html
I'm using Swagger2 with Springfox and Spring Boot. I have an endpoint defined like so:
#ApiOperation(value = "save", nickname = "Save Store")
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Created"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 500, message = "Failure", response = ErrorResource.class)})
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public void save(#Valid #RequestBody Store store, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
throw new InvalidRequestException("Invalid Store", bindingResult);
}
this.storeService.save(store);
response.setHeader("Location", request.getRequestURL().append("/").append(store.getId()).toString());
}
The generated API docs are showing the id of Store in the Model Schema. Technically, when creating a Store the JSON should not contain the id. I'm trying to figure out how to tell Swagger/Springfox to ignore the id but only for this endpoint.
You can hide a field from a model by annotating the property of the class with #ApiModelProperty and setting its hidden property to true.
import io.swagger.annotations.ApiModelProperty;
public class Store {
#ApiModelProperty(hidden = true)
private Long id;
}
Unfortunately, by doing so, you will hide the id field on every endpoint which uses the Store class as an input. Showing the field for another endpoint would require a separate class.