How to add JAX-RS Response in OpenAPI YAML file - rest

I have a REST service and I have defined the API signature using OpenAPI's YAML file.
Something like,
title: Sample Pet Store App
description: This is a sample server for a pet store.
termsOfService: http://example.com/terms/
contact:
name: API Support
url: http://www.example.com/support
email: support#example.com
paths:
v1/employees/{employeeId}:
get:
responses:
'200':
content:
....
From the YAML file, I generate the API requests using something like OpenAPI generator.
But how do I specify a https://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Response.html, in my YAML file?
This is how I want to send a response from my Java code. I want to know how I can add this Response object to OpenAPI's YAML?
import javax.ws.rs.core.Response;
#Path("/v1/employees")
public Response getEmployee(String employeeId) {
// ...
return Response
.status(Response.Status.OK)
.entity(employee)
.build();
}
I am new to REST API development. I checked the documentation, but couldn't find details in OpenAPI's on how to add a Javax Response.

Depends on the template you use, by default it is not there, but you can create a custom template to use.
here is the list of available templates.
you must specify type of response you return on OpenAPI spec. like this:
v1/employees/{employeeId}:
get:
operationId: getUser
responses:
200:
description: Return user
content:
application/json:
schema:
$ref: '#/components/schemas/UsertResponseDTO'
after that, if you use default template, add manually typo response like this:
import javax.ws.rs.core.Response;
#Path("/v1/employees")
public Response getEmployee(String employeeId) {
// ...
return Response.ok(employee).build();
}

To solve my problem, instead of returning a Response object, I went with throwing a javax.ws.rs.WebApplicationException and adding an ExceptionTranslator code to convert all my exceptions into a WebApplicationException.
Here is a sample code on the exception translation.
// Actual code to convert Java exception into javax.ws.rs.WebApplicationException.
catch (Exception e) {
throw new WebApplicationException(getResponse(e));
}
// Exception translation code sample. This can be made into a nice generic function to handle different types of exceptions.
Response getResponse(Throwable t) {
if (throwable instanceof NotFoundException) {
Error error = new Error();
error.setType("404");
error.setMessage("Requested entry could not be found");
Response.status(Status.NOT_FOUND)
.entity(error)
.build();
}
}

Related

Spring Boot REST API Endpoint Mapping best practice

I am using below endPoint URL Mapping with HTTP Methods like ( POST, DELETE, GET, PUT)
POST for Create a new Trade -
#PostMapping("/trade")
DELETE for Delete a Trade with specific id -
#DeleteMapping("/trade/{id}")
GET for Get details of specific Trade -
#GetMapping("/trade/{id}")
PUT for Update Trade details -
#PutMapping(“/trade/{id}”)
GET for Retrieve all Trade list of the collection -
#GetMapping("/trades")
Spring currently supports five types of inbuilt annotations for handling different types of incoming HTTP request methods which are GET, POST, PUT, DELETE and PATCH. These annotations are:
#GetMapping
#PostMapping
#PutMapping
#DeleteMapping
#PatchMapping
From the naming convention, we can see that each annotation is meant to handle the respective incoming request method types, i.e. #GetMapping is used to handle GET type of request method, #PostMapping is used to handle POST type of request method, etc.
if I am missing anything here Please suggest
Add API version like
#RestController
#RequestMapping("/API/V1")
public class TestController {
#RequestMapping("/greeting")
public String greeting( {
return "welcome";
}
}
For versioning there are several approaches you can use:
URI path:
Include the version number in the URL path of the endpoint.
For example v1 in /api/v1/trade:
public class TradeController {
#GetMapping("v1/trade")
public Trade tradeVersionOne() {
return new Trade("123","Trade Result");
}
#GetMapping("v2/trade")
public Trade tradeVersionTwo() {
return new Trade(new RealTimeTrade("123", "Real Time Trade Result"));
}
}
Query parameters:
Pass the version number as a query parameter with a specified name.
For example: ?version=1 in /api/trade?version=1:
public class TradeController {
#GetMapping(name = "v1/trade", params = "version=1")
public Trade tradeVersionOne() {
return new Trade("123","Trade Result");
}
#GetMapping(name = "v2/trade", params = "version=2")
public Trade tradeVersionTwo() {
return new Trade(new RealTimeTrade("123", "Real Time Trade Result"));
}
}
Custom HTTP headers:
Define a new header that contains the version number in the request.
Content negotiation:
Include the version number in the Accept header along with the accepted content type.

Wiremock: choose body file name based on request parameter

I am using wiremock to mock http server and I am returning responses from json files (using withBodyFile method).
Now I want to choose and return response json file based on request parameter. For the example below, I want to define one stub so that the body file is chosen based on request parameter.
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key1"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_key1.json")
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key2"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_key2.json")
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key3"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_key3.json")
Any idea how this would be possible? I tried defining transformer but it was not possible to get Stream Source Path from Response object in overridden method so can't use that appraoch. Thanks a lot..
Body File name can't be parameterized in wiremock as of now. I had a similar requirement, I needed to return the file based on the request URL. So I implemented a transformer as below:
public class BodyFileNameResponseTransformer extends ResponseDefinitionTransformer {
public ResponseDefinition transform(Request request, ResponseDefinition rd,
FileSource fileSource, Parameters parameters) {
if (rd.getBodyFileName().startsWith("{{")) {
return new ResponseDefinitionBuilder().**withBodyFile**(request.getUrl().substring(1))
.withStatus(rd.getStatus())
.withHeaders(rd.getHeaders())
.withTransformers(
rd.getTransformers().toArray(new String[rd.getTransformers().size()]))
.build();
}
return rd;
}
public String getName() {
return "BodyFileNameTransformer";
}
}
you can use request.queryParameter(key) instead of request.getUrl() and form any
file path. Create the file path based on your need and set it as bodyFile on returned ResponseDefinition.
Don't forget to start wiremock with --extensions: Extension class names
More details at Extending Wiremock
This is possible by using the inbuilt template helpers provided by Handlebar.
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key3"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_{{request.query.key}}.json")
Check for the various models available at http://wiremock.org/docs/response-templating/.

Swagger-generated Spring controller recievs NULLs in the body parameter of POST

My API-first swagger client/server transfers POST request body parameter object incorrectly. Spring (4.3.14.RELEASE) server receives nulls in the #RequestBody parameter of the POST request.
The key difference between requests passed over SwaggerUI and over generated
typescript client is that SwaggerUI passes object fields as query parameters (Spring handles well), but the generated typescript client over json body (Spring handles nulls).
It seems like autogenerated client sends parameter over body, but the autogenerated server expects parameters in the query.
The swagger fragment:
/project:
post:
security:
- Bearer: []
tags:
- Project
summary: Create new project
operationId: createProject
consumes:
- "application/json"
- "text/json"
parameters:
- name: project
in: body
description: project value
schema:
$ref: '#/definitions/ProjectRequestDTO'
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/ProjectDTO'
default:
description: Bad requst
The resulting Spring MVC method:
ApiOperation(value = "Create new project", nickname = "createHachathon", notes = "", response = ProjectDTO.class, authorizations = {
#Authorization(value = "Bearer")
}, tags={ "Project", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successful response", response = ProjectDTO.class),
#ApiResponse(code = 200, message = "Bad requst") })
#RequestMapping(value = "/project",
consumes = { "application/json", "text/json" },
method = RequestMethod.POST)
default ResponseEntity<ProjectDTO> createHachathon(#ApiParam(value = "project value" ) #Valid #RequestBody ProjectRequestDTO project) {
I'm sending request from typescript over manually hacked client (title parameter added as demonstration that this parameter will be unmarshalled well):
return this.httpClient.post<ProjectDTO>(`${this.basePath}/project?title=hello`,
The resulting request in the Chrome console:
Unfortunatelly Spring receives only the value which were passed over query parameter but not over request body:
Plese help me to make my swagger-codegen-maven-plugin 2.3.1 produce client/server swagger API which will transfer data transparently.
The root cause is that Spring ignores method parameter annotations of the implemented Interface, so the #RequestBody was ignored in my Controller implementation.
The <delegatePattern>true</delegatePattern> parameter inside <configOptions> section of swagger-codegen-maven-plugin resolved this problem.
This parameter makes Swagger generate invocation of delegate method inside default implementation of API Interface methods. My Controller implements this method and overrides this delegate mathods, so the source method annotations are in safe.
Sorry, may be late answer. maybe helps someone.
I was hitting same issue while using spring boot+Swagger2.
My REST API with POST method :
public void getAllNames(#ApiParam(name = "Get All names Request",
value = "Request to get all names",
required = true) #RequestBody BasicCredentialsJsonObject requestJson)
I had to introduce Json annotations(JsonPropertyOrder, JsonProperty) on below class to get going. I was hitting issue till I made this changes.
BasicCredentialsJsonObject :
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"password",
"username"
})
public class BasicCredentialsJsonObject {
#JsonProperty("username")
private String username;
#JsonProperty("password")
private String password;
}
Sometimes, it seems stupid but I had issue with my request body, it was null into the controller. What I changed, the import, I was using the swagger.requestBody package instead of the springframeWork.request import.

Error returning custom object via GET method

I am trying to run a simple example in Jersey which has a GET method and returns a Custom object.
on calling GET I am getting following error
MessageBodyWriter not found for media type=text/plain
I have looked into few answers on stackoverflow where they are suggesting it to put a default constructor and jackson dependency in pom.xml. I have already done that but no luck. Can some one please suggest what I am doing wrong.
Resource Class
#Path("customdatatyperesource")
public class CustomDataTypeResource {
#GET
public CustomResource1 get(#Context UriInfo ui) {
return new CustomResource1();
}
}
Custom Class
#XmlRootElement
public class CustomResource1 {
#XmlElement
String res;
public CustomResource1() { }
#Override
public String toString(){
return "Custom : "+res;
}
}
pom.xml
Thanks
So I figured out that error is not in the code but in the request sent.
When I send the request with header accept: text/plain
I am getting the error MessageBodyWriter not found for media type=text/plain
The resolution is accept header needs to match with what resource can produce.
In this case our resource is capable of producing XML or JSON
A better and more comprehensive way to write this code would be to put produce annotation on the methods.
#Produces(MediaType.TEXT_XML)
and put correct accept header such as
accept: application/json

#BeanParam gives exception A message body reader for Java class was not found

I am trying to make a jersey based web service. In this if i take input params using #FormParam it works fine:
#POST
#Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, "application/x-www-form-urlencoded"})
#Path("/registeruser")
public Response registerUser(#FormParam ("email") String email,#FormParam ("name") String name ){
System.out.println("Inside register device");
System.out.println("registered" + email);
return null;
}
but when I try using #BeanParam it does not works and gives me an exception
#POST
#Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, "application/x-www-form-urlencoded"})
#Path("/registeruser")
public Response registerUser(#BeanParam UserForm userForm ){
System.out.println("Inside register device");
service.registerUser(userForm);
System.out.println("registered" + userForm.getEmail());
return null;
}
A message body reader for Java class com.stc.dms.forms.UserForm, and Java type class com.stc.dms.forms.UserForm, and MIME media type application/octet-stream was not found.
You don't need to use #BeanParam to pass an object as input. Just pass it like this :
#POST
#Path("register")
#Consumes(MediaType.APPLICATION_JSON)
public Response registerUser(UserForm dto) {
// ...
}
Just make sure to include the libraries for producing/consuming json. If the client is in javascript you don't need anything else (just use JSON.stringify() on your form object), for the server add some json libraries such as Jackson
EDIT :
If you want to stick with #BeanParam, take a look at this tutorial here. Basically it seems that you don't need to specify the mediatype, as Jersey will handle that for you.