FeignClient API with different response class - java

I am using Jhipster. I have a yaml file, then generate java code using jhipster openapi-client. It generate several files, including the all the model class needed (to contain the request and response).
DefaultApiClient
#FeignClient(name="${default.name:default}", url="${default.url:https://test.api.com/testing}", configuration = ClientConfiguration.class)
public interface DefaultApiClient extends DefaultApi {
}
DefaultApi
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2021-01-22T14:50:31.377193700+08:00[Asia/Singapore]")
#Validated
#Api(value = "Default", description = "the Default API")
public interface DefaultApi {
/**
* POST /req/v1 : This is the request
*
* #param authorization JWT header for authorization (required)
* #param body (required)
* #return successful operation (status code 200)
* or server cannot or will not process the request (status code 400)
*/
#ApiOperation(value = "This is the request", nickname = "Verification", notes = "", response = ResponseType.class, authorizations = {
#Authorization(value = "clientID")
}, tags={ })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "successful operation", response = ResponseType.class),
#ApiResponse(code = 400, message = "server cannot or will not process the request", response = ServiceMessagesType.class) })
#RequestMapping(value = "/req/v1",
produces = "application/json",
consumes = "application/json",
method = RequestMethod.POST)
ResponseEntity<ResponseType> Verification(#ApiParam(value = "JWT header for authorization" ,required=true, defaultValue="Bearer REPLACE_THIS_KEY") #RequestHeader(value="Authorization", required=true) String authorization,#ApiParam(value = "" ,required=true ) #Valid #RequestBody RequestType body);
}
I can manage to get the response successfully, but the problem appear when I send a false request, It will response with and Bad Request 400 and crash my program.
As you can see on the swagger annotation #ApiResponse, it return different class.
I am really confuse with it. My question is:
Just for confirm, #ApiResponse is only for documentation, right? Does this code affect the program like when it return code 400, the response will automatically be ServiceMessageType class?
How can I handle different response class? As you can see in the function deffinition, ResponseEntity Verification, it will return ResponseType as the body of ResponseEntity. But when I send an error request to this Api, this Api will return ServiceMessageType. And fyi, the code 400 will give my program an error says "failed and no fallback available" so I think I need an error handle to do it.
For no.2, I already search for the solution in several source
https://programmer.group/feign-call-error-failed-and-no-fallback-available.html
but I don't really get it. I use the fallbackFactory, and it can handle the 400 code exception. But I still really confuse about how to return different response class. And I get the result not in correct structure, as the link said:
By implementing FallbackFactory, you can get the exception thrown by the service in the create method. However, please note that the exception here is encapsulated by Feign, and the exception thrown by the original method cannot be seen directly in the exception information. The abnormal information obtained is as follows: status 500 reading TestService#addRecord(ParamVO); content: {"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}
To illustrate, in this example, the interface return information of the service provider will be uniformly encapsulated in the user-defined class Result, and the content is the above content: {"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}
Please explain to me how it work, or you can give me a link about how it works, I will really appreciate the help.

Related

How to parse request body of POST request, if client set content-type="application/xml" and body=null?

I have POST REST endpoint, which initially doesn't expect any request body. I want to add optional request body for this API. Optional, means I want all clients, which used this API with content-type="application/xml" and empty body, to continue using API in the same manner, without rewriting any single row of code. For content-type="application/json", it works fine, no need client to alter his code. But for "application/xml", clients now receive "400 Bad request" response. Is there any chance to introduce optional request body and support all existing API clients?
REST endpoint is written in java, and annotations from "javax.xml.bind.annotation" package are used to declare object model
Below is signature of endpoint, defined with swagger annotations. dueDatesDetails - object that is supposed to be added as optional:
#POST
#Path("/sites/{siteid}/filestatus")
public Response linkContent(
#ApiParam(value = "site id", required = true) #PathParam("siteid") String siteId,
#ApiParam(value = "status", required = true) #QueryParam("status") String status,
DueDatesDetails dueDatesDetails) {
Here is java object model, that must be optional:
#XmlRootElement(name = "duedatesdetails")
#XmlAccessorType(XmlAccessType.FIELD)
public class DueDatesDetails
{
#Getter
#Setter
#XmlElement(name = "duedatedetails")
#JsonProperty("duedatesdetails")
private List<DueDateDetails> dueDateDetailsList;
}
You can't use request body as a method parameter then. I believe the framework you are using (most likely Spring) doesn't have optional request body feature. You have to manually get the payload from request stream & process it.
It should be something like:
#POST
#Path("/sites/{siteid}/filestatus")
public Response linkContent(
#ApiParam(value = "site id", required = true) #PathParam("siteid") String siteId,
#ApiParam(value = "status", required = true) #QueryParam("status") String status, HttpServletRequest request) {
byte[] payload = IOUtils.toByteArray(request.getReader(), request.getCharacterEncoding());
String body = new String(payload, request.getCharacterEncoding());
if(body == null){
// do something
} else {
// parse body to convert to xml object dueDatesDetails
}

Method is not being detected when using the Custom class as input parameter type for the REST API service

I am developing a Web application using Vuejs/Nuxtjs which makes call to my Java Service using the Axios but during the call I get the error:
POST http://localhost:9001/generate 500 (Internal Server Error)
I am getting this error because my Java service type accepts the input parameter of Custom data type InputParameter. If I change to String then it works fine. So I would like to know what changes should I make to front-end call or to my Java Service so it can work with InputParameter type.
Following is the Vuejs call that I am making to Java service:
const headers = { 'Content-Type': 'application/json' }
this.$axios.post('/generate', { ...JSON.parse(inputParameter) }, { headers })
.then((response) => {
console.log(JSON.stringify(response.data))
})
.catch((error) => {
console.log(error)
})
Following is my Java service method which is NOT working with custom data type InputParameter, the call does not detect the method and execution does not go within the method:
#Path("/generate")
#Produces(MediaType.APPLICATION_JSON)
#APIResponses(value = {
#APIResponse(responseCode = "200", description = "returns list of JSON Objects"),
#APIResponse(responseCode = "500", description = "An internal Server Error occurred")
})
public String generate(final InputParameter inputParameter){
System.out.println(inputTemplate.toString());
return null;
}
If I change the above JAVA Service method input parameter data type to String then the method is detected and input is printed:
#Path("/generate")
#Produces(MediaType.APPLICATION_JSON)
#APIResponses(value = {
#APIResponse(responseCode = "200", description = "returns list of JSON Objects"),
#APIResponse(responseCode = "500", description = "An internal Server Error occurred")
})
public String generate(final String inputParameter){
System.out.println(inputTemplate);
return null;
}
I am not understanding whats wrong here. Can someone please help?
Things I have tried:
Adding #Consumes(MediaType.APPLICATION_JSON).
Changing the method to public String generate(#RequestBody final InputParameter inputParameter)
My InputParameter class looks something like this:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class InputParameter {
private List<String> names;
private List<String> jobs;
}
My InputParameter which I am passing to Java Service looks something like this:
{
"names":[
"Batman",
"Superman",
"Ironman"
],
"jobs":[
"Fighting",
"Fyling",
"Teching"
]
}
Dear in the back end the api is accepting an object of type InputParameter. For solving the problem you have to create a class the same as InputParameter class and generate an object of that and send that object to the back end.
Let me know if you need more help!
Posting the answer can be helpful to someone else in the future. I tried some things but nothing worked and finally following worked for me:
#POST
#Path("/generate")
#Produces(MediaType.APPLICATION_JSON)
#APIResponses(value = {
#APIResponse(responseCode = "200", description = "returns list of JSON Objects"),
#APIResponse(responseCode = "500", description = "An internal Server Error occurred")
})
public String generate(final InputParameter inputParameter){
System.out.println(inputTemplate.toString());
return null;
}
There was also one small setting that I had to change related to Jackson ObjectMapper which is not relevant here because it's my project-specific that I missed in another class. Maybe that was the issue I was facing. Now everything is working as expected.

How to Inject dynamic object in response class of #ApiOperation for Swagger

Am developing a project to handle CRUD operation for 50+ entities using Spring boot REST api with Swagger documentation.
For all the CRUD operation i have a common response which can handle GET,UPDATE,POST and DELETE.
Am using #ApiOperation as shown below for swagger documentation, Below is just an example for Client entity,
#ApiOperation(value = "Get Client details API", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE,
httpMethod = "GET", response = InternalApiResponse.class)
#ApiResponses(value = { #ApiResponse(code = HttpURLConnection.HTTP_OK, message = HttpStatusMessages.MESSAGE_200),
#ApiResponse(code = HttpURLConnection.HTTP_UNAUTHORIZED, message = HttpStatusMessages.MESSAGE_401),
#ApiResponse(code = HttpURLConnection.HTTP_FORBIDDEN, message = HttpStatusMessages.MESSAGE_403),
#ApiResponse(code = HttpURLConnection.HTTP_NOT_FOUND, message = HttpStatusMessages.MESSAGE_404) })
#RequestMapping(value = "/clients", headers = "Accept=application/json", method = RequestMethod.GET)
public ResponseEntity<InternalApiResponse> getClients(#ModelAttribute RequestParams requestParams,
#ModelAttribute Client client) throws InternalApiException {
}
InternalApiResponse.class is as shown,
InternalApiResponse.class
{
private Status status;
private Error error;
private Object entityObject;
}
in the above class, entityObject needs to be dynamically considered based on input entity (ex: #ModelAttribute Client client)
Swagger UI is as shown :
sample screenshot
From Java side i can inject required object in response at run time, But how i can inject the required object to InternalApiResponse for Swagger #ApiOperation, so that can i see the Api response with particular entity details in Swagger UI...Please help

How to extract a variable value in Spring AOP advise

The authentication method has been integrated with every REST calls in the API. I have been trying to implement an authentication method via Spring AOP so that I can remove all the duplicate code from end-points and have one single advise to look for all public methods in Controllers.
Please check the below my code,
#Aspect
public class EndpointAccessAspect {
/**
* All the request mappings in controllers need to authenticate and validate end-point access
*/
#Before("execution(public * com.xxxx.webapi.controllers.MenuController.getCategory(HttpServletRequest)) && args(request)")
public void checkTokenAccess(HttpServletRequest request){
String re =(String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
System.out.println(" %%%%%%%%%%%%%%%%%%% checkTokenAccess %%%%%%%%%%%%%%%" + re);
}
public void checkEndPointPermission(){
System.out.println(" $$$$$$$$$$$$$$$$$$ checkEndPointPermission &&&&&&&&&&&&&");
}
}
However, I saw Intelij gives error near getCategory(HttpServletRequest)) && args(request) saying can not resolve symbol HttpServletRequest. I need the request to distingues each REST end-points. There are more variables than HttpServletRequest variable in the method but only that variable is needed.
The code is compiling when I test the functionality I noticed it doesn't reach to the advise. Can anybody help me to fix this?
I found this from Spring documentation
Spring doc
any join point (method execution only in Spring AOP) which takes a
single parameter, and where the argument passed at runtime is
Serializable
Does this mean I can not use methods that have multiple parameters?
Controller end-point
#RequestMapping(value = "{menuId}/categories/{categoryId}", method = RequestMethod.GET)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successful retrieval of a category requested", response = ProductGroupModel.class),
#ApiResponse(code = 500, message = "Internal server error") })
public ProductGroupModel getCategory(
#ApiParam(name = "menuId", value = "Numeric value for menuId", required = true) #PathVariable(value = "menuId") final String menuId,
#ApiParam(name = "categoryId", value = "Numeric value for categoryId", required = true) #PathVariable(value = "categoryId") final String categoryId,
final HttpServletRequest request) {
The following syntax, resolved the above issue. Basically, I had to modify the code to deal with multiple parameters in the advise.
#Before("execution(public * com.xxxx.webapi.controllers.MenuController.getCategory( HttpServletRequest,..)) && args(request, ..)")
public void checkTokenAccess(HttpServletRequest request){
String re =(String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
System.out.println(" %%%%%%%%%%%%%%%%%%% checkTokenAccess %%%%%%%%%%%%%%%" + re);
}

Spring boot returning 200 status code for custom error pages

I want to override the whitelabel error page. So as an example I have done this simple class:
#RestController
public class MyCustomErrorController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return "This is the error page";
}
#Override
public String getErrorPath() {
return PATH;
}
}
I have taken my example from here:
https://gist.github.com/jonikarppinen/662c38fb57a23de61c8b
According to that gist, it actually has a comment like this:
// Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
// Here we just define response body.
However that's not what I'm seeing. For instance if I hit to a URL that I know that it should respond me a 500 status code (intentional NullPointerException), then that's what I should see, but when I hit to that URL I get a 200 response back with my error message ("This is the error page")
If I don't use this custom controller, then it shows me a 500 error page with the stacktrace on it, which is the default behavior. I have seen an old issue opened in 2014 here:
https://github.com/spring-projects/spring-boot/issues/684 that someone mentioning the same problem, however their solution is to show explicitly 500 responses, which does not really pass through the HTTP response code.
Just for the record, I actually put a breakpoint to org.apache.catalina.connector.Response.sendError() method. When this custom error controller does not exist, I can clearly see that sendError() method is being called with a status 500. However if I were to add HttpServletResponse argument to my error() method I do not see that the instance has 500 status code set.
Explanation
In the website example you provided, the HttpStatus is retrieved from the injected HttpServletResponse.
So the following:
Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
means
Spring sets it on the HttpServletResponse that he gets injected into his method as
argument.
He then has to retrieve the status and set it on his model ErrorJson.
Solution
To follow your example, you could change your method to this:
#RequestMapping(value = ERROR_MAPPING)
public ResponseEntity<String> error(HttpServletResponse response) {
return new ResponseEntity<String>("This is the error page",
HttpStatus.valueOf(response.getStatus()));
}
I used ResponseEntity<String> instead of defining a custom object (a.k.a. ErrorJson).
As I believe you know, alternatively to using the HttpServletResponse's status, you could just set yours with HttpStatus.
Here you are simply returning a message from one method, which is not an error as per SpringBoot.
Following method will help you to return http status code as you want :
#RequestMapping(value = PATH)
public ResponseEntity<Map<String, Object>> error() {
Map<String, Object> map = new HashMap<>();
String statusMessage = "This is the error page";
String statusCode = HttpStatus.BAD_REQUEST.value();
map.put(STATUS_CODE, statusCode);
map.put(STATUS_MESSAGE, statusMessage);
return ResponseEntity.badRequest().body(map);
}

Categories

Resources