Retrofit POST request to Spring Boot service - java

I am using Retrofit for post request to Spring Boot service but always is called failure Callback method. This is my simplified code:
Spring Boot service (Controller):
#RestController
public class ServController {
#Autowired
private UserRepository userRepository;
#RequestMapping(value = "/user", method = RequestMethod.POST)
public Boolean signUpUser(#RequestBody User user)
{
return true;
}
}
My client interface:
public interface ChainApi {
public static final String USER_PATH = "/user";
#POST(USER_PATH)
public void signUpUser(#Body User user, Callback<Boolean> callback);
}
Async POST request:
User user = new User();
user.setId(12);
user.setName(nameEtx.getText().toString());
user.setEmail(emailEtx.getText().toString());
user.setPassword(passwordEtx.getText().toString());
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constant.URL_LOCALHOST)
.build();
ChainApi service = restAdapter.create(ChainApi.class);
service.signUpUser(user, new Callback<Boolean>() {
#Override
public void success(Boolean aBoolean, Response response) {
Log.i(TAG, "Succesfull");
#Override
public void failure(RetrofitError error) {
Log.i(TAG, "Error " + error.getMessage()); // 400 Bad Request
}
});
This is my User class(POJO):
#JsonIgnoreProperties(value = { "additionalProperties"})
public class User {
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
#JsonProperty("password")
private String password;
#JsonProperty("email")
private String email;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
// methods
}
NOTES: I am developing in Android and when make manual POST request from Postman I get 200 OK.
In addition I get in logcat message: 400 Bad Request

Retrofit uses GSON as its default Converter. #JsonIgnoreProperties is an annotation from Jackson. Looking at your RestAdapter you don't seem to be specifying a Jackson Converter.
Square has implemented a JasksonConverter, you use it by including the dependency.
compile 'com.squareup.retrofit:converter-jackson:X.X.X'
Use the version that matches your Retrofit version.
Then
JacksonConverter converter = JacksonConverter(new ObjectMapper());
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constant.URL_LOCALHOST)
.setConverter(converter)
.build();

Related

Spring Boot & WebClient - how to add additional headers using #ConditionalOnProperty to a specific WebClient instance?

I have a service that is holding an WebClient instance:
#Service
#Getter
public class SomeApiWebClient {
private final WebClient webClient;
private final String someApiUrl;
public SomeApiWebClient(#Value("${some.api.url}") String someApiUrl) {
this.someApiUrl= someApiUrl;
this.webClient = WebClient.builder()
.baseUrl(someApiUrl)
.defaultHeaders(getDefaultHttpHeaders())
.build();
}
public Consumer<HttpHeaders> getDefaultHttpHeaders() {
return headers -> {
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
};
}
}
To call this API from UAT/PROD environments - this is just fine. But to call it from our local machines, we need to use Client-ID and Secret HTTP headers:
public Consumer<HttpHeaders> getDefaultHttpHeaders() {
return headers -> {
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Client-Id", "someId");
headers.add("Secret", "someSecret");
};
}
I have put the someId and someSecret values into application-dev.properties:
some.api.client-id=someId
some.api.token=someSecret
Now I would like to use #Configuration combined with #ConditionalOnProperty({"some.api.client-id", "some.api.token"}) to intercept and add a filter to MDSWebClient that will add those headers when the some.api.client-id and some.api.token are present.
I have tried doing something like this:
#Configuration
#ConditionalOnProperty({"some.api.client-id", "some.api.token"})
public class MDSWebClientInterceptor implements WebFilter {
#Value("some.api.client-id")
private String clientId;
#Value("some.api.token")
private String token;
private static final String CLIENT_ID_HEADER = "Client-Id";
private static final String CLIENT_TOKEN_HEADER = "Secret";
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(
exchange.mutate().request(
exchange.getRequest().mutate()
.header(CLIENT_ID_HEADER, clientId)
.header(CLIENT_TOKEN_HEADER, token)
.build())
.build());
}
}
But this doesn't work at all, and I have a hunch that if it would work, it would affect ALL WebClient instances, not just the one in SomeApiWebClient.
Is something like this even possible?

Provide different ClientInterceptor per request using Spring Web Services

I've created a custom web service client by extending WebServiceGatewaySupport and also implement custom ClientInterceptor to log some request/response data.
I have to create new interceptor for every call because it has to store some data about the request.
The problem occurs when I make two or more calls to my client. The first request applies its own interceptor with its clientId. The second should do the same. But since both requests use the same WebServicetemplate in my client, the second request replaces the interceptor with its own, with its clientId there.
As a result, I should get the following output to the console:
Request: clientId-1
Request: clientId-2
Response: clientId-1
Response: clientId-2
But I got this:
Request: clientId-1
Request: clientId-2
Response: clientId-2
Response: clientId-2
Here is come code examples (just for understanding how it should work):
#Data
class Response {
private final String result;
public Response(String result) {
this.result = result;
}
}
#Data
class Request {
private final String firstName;
private final String lastName;
}
#Data
class Context {
private final String clientId;
}
#Data
class Client {
private final String clientId;
private final String firstName;
private final String lastName;
}
class CustomInterceptor extends ClientInterceptorAdapter {
private final String clientId;
public CustomInterceptor(String clientId) {
this.clientId = clientId;
}
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Request: " + clientId);
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Response: " + clientId);
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Error: " + clientId);
return true;
}
}
#Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor(context.getClientId())};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate().marshalSendAndReceive(request);
}
}
#Service
#RequiredArgsConstructor
class CustomService {
private final CustomClient customClient;
public String call(Request request, Context context) {
Response response = customClient.sendRequest(request, context);
return response.getResult();
}
}
#RestController
#RequestMapping("/test")
#RequiredArgsConstructor
class CustomController {
private final CustomService service;
public CustomController(CustomService service) {
this.service = service;
}
#PostMapping
public String test(#RequestBody Client client) {
Request request = new Request(client.getFirstName(), client.getLastName());
Context context = new Context(client.getClientId());
return service.call(request, context);
}
}
Is it possible to implement custom interceptors with some state for each call? Preferably without any locks on WebServicetemplate to avoid performance degradation.
Okay. I've found the solution for my case.
I've created an implementation of WebServiceMessageCallback and using it I'm saving data of each request not in interceptor but in WebServiceMessage's mime header.
#Data
class CustomMessageCallback implements WebServiceMessageCallback {
private final String clientId;
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
headers.addHeader("X-Client-Id", clientId);
}
}
And pass this callback in my client implementation:
#Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor()};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate()
.marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
}
}
So now I can get this data while processing request/response/error via interceptor.
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String clientId = ((SaajSoapMessage) messageContext.getRequest())
.getSaajMessage()
.getMimeHeaders()
.getHeader("X-Client-Id")[0];
System.out.println("Request: " + clientId);
return true;
}

Add created date time to a REST API using Swagger

I have a couple of APIs and using springfox-swagger for API documentation.
I have a requirement to add the creation date to the respective API. How can I achieve this using swagger. I don't need any API versioning.
Ex:
#ApiOperation(value = "Creates a new user and returns the created user.")
#PostMapping(/user)
public ResponseEntity<UserDto> createUser(#RequestBody UserDto userDto) {
User user =userService.create(userDto);
return new ResponseEntity<>(UserMappers.USER_ENTITY_TO_DTO.apply(user),HttpStatus.CREATED);
}
In the above example, I want to add the creation date of /user so that I can trace the creation date.
In my project I have a similar requirement. As a solution I have created a custom annotation (for marking the endpoint) and wrote a plugin (for updating the API description).
Option #1
#ApiSince annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ApiSince {
String value() default "";
}
ApiSincePlugin plugin:
#Component
public class ApiSincePlugin implements OperationBuilderPlugin {
private final DescriptionResolver resolver;
#Autowired
public ApiSincePlugin(DescriptionResolver resolver) {
this.resolver = resolver;
}
#Override
public void apply(OperationContext context) {
final String sinceTemplate = "### Since %s%n%n%s";
String notes = "";
Optional<ApiOperation> apiOperationOptional = context.findAnnotation(ApiOperation.class);
if (apiOperationOptional.isPresent()) {
notes = apiOperationOptional.get().notes();
}
String finalNotes = notes;
Optional<ApiSince> apiSinceOptional = context.findAnnotation(ApiSince.class);
if (apiSinceOptional.isPresent()) {
finalNotes = String.format(sinceTemplate, apiSinceOptional.get().value(), notes);
}
context.operationBuilder().notes(resolver.resolve(finalNotes));
}
#Override
public boolean supports(DocumentationType type) {
return true;
}
}
#ApiSince in action:
#ApiSince(value = "2019-10-31")
#PostMapping(value = "/login")
#ApiOperation(value = "Authenticate user", nickname = "login", notes = "your API description")
#ResponseStatus(HttpStatus.OK)
#ApiResponses(value = {
#ApiResponse(code = 200, response = LoginResponse.class, message = HTTP_200_OK),
...
})
#ResponseBody
ResponseEntity<LoginResponse> login(...);
If you don't want do add it in the description but as an extra JSON attribute then take a look at this solution: Custom Operation Builder Plugin
.
Option #2
#ApiSince annotation (code same as above)
ApiSincePlugin plugin:
#Component
public class ApiSincePlugin implements OperationBuilderPlugin {
#Override
public void apply(OperationContext context) {
Optional<ApiSince> annotation = context.findAnnotation(ApiSince.class);
if (annotation.isPresent()) {
String value = annotation.get().value();
ObjectVendorExtension extention = new ObjectVendorExtension("x-since");
extention.addProperty(new StringVendorExtension("value", value));
context.operationBuilder().extensions(Collections.singletonList(extention));
}
}
#Override
public boolean supports(DocumentationType documentationType) {
return true;
}
}
Activate extensions in the Swagger UI:
#Bean
UiConfiguration uiConfig() {
return UiConfigurationBuilder
.builder()
.showExtensions(true)
...
.build();
}
#ApiSince in action (code same as above):

org.springframework.web.client.HttpClientErrorException 400 RestTemplate.postForEntity

I am consuming API which has to type of response success response 200 and Bad response 400 both of them has parameters inside their response body but the problem is am not able to get the bad response parameters it throws this exception
public ResponseEntity<String> balanceInquiry(BalanceInquiryRequestDto balanceInquiryRequestDto) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.set("API-KEY", "5d6f54d4");
HttpEntity<BalanceInquiryRequestDto> request = new HttpEntity<BalanceInquiryRequestDto>(balanceInquiryRequestDto , httpHeaders);
ResponseEntity<String> postForEntity =
restTemplate.postForEntity(uri , request, String.class);
return postForEntity;
}
it is working good when the response is ok 200
I created a small spring boot project to showcase what you can do.
First a simple service that will give us an error when called:
#RestController
public class Endpoint {
#GetMapping("/error")
public ResponseEntity createError() {
ErrorDetails errorDetails = new ErrorDetails("some error message");
return ResponseEntity.status(400).body(errorDetails);;
}
}
The error details which you want to extract are similar to this in this example:
#AllArgsConstructor
#Getter
#Setter
#ToString
#EqualsAndHashCode
public class ErrorDetails {
private String errorMessage;
}
And then another endpoint with a client that calls the failing service. It returns the error details received:
#RestController
public class ClientDemo {
#Autowired
private RestTemplate restTemplate;
#GetMapping("/show-error")
public String createError() {
try{
return restTemplate.getForEntity("http://localhost:8080/error", String.class).getBody();
} catch(HttpClientErrorException | HttpServerErrorException ex) {
return ex.getResponseBodyAsString();
}
}
}
For sake of completion:
#SpringBootApplication
public class StackoverflowApplication {
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
When navigating to http://localhost:8080/show-error you see this:
{
"errorMessage": "some error message"
}

How to validate path variables in REST

My Question is How can I validate request parameters if I use #PathParam.
For instance I have two request parameters, name and id
path is localhost:/.../search/namevalue/idvalue
if a user submits blanks for name or id I should send a response mentioning that name is required/ id is required.
I could do the validations if I use #QueryParam, but I'm not sure how to do it if I have to use pathvariables.
If I just test using http:/localhost:/.../search/namevalue orhttp:/localhost:/.../search/idvalue or http:/localhost:/.../search/ it's throwing servlet exception.
Below is the code, if i use QueryParams validations work just fine, Please let me know the approach when i use pathparam
#Controller
#Path("/customer")
public class CustomerController extends BaseController implements Customer {
#Override
#GET
#Produces({ "application/json", "application/xml" })
#Path("/search/{name}/{id}/")
public Response searchCustomerDetails(
#PathParam("name") String name,
#PathParam("id") Integer id) {
ResponseBuilder response = null;
CustomerValidations validations = (CustomerValidations) getAppContext()
.getBean(CustomerValidations.class);
CustomerResponse customerResponse = new CustomerResponse();
CustomerService customerService = (CustomerService) getAppContext()
.getBean(CustomerService.class);
try {
validations.searchCustomerDetailsValidation(
name, id,customerResponse);
if (customerResponse.getErrors().size() == 0) {
CustomerDetails details = customerService
.searchCustomerDetailsService(name, id);
if (details == null) {
response = Response.status(Response.Status.NO_CONTENT);
} else {
customerResponse.setCustomerDetails(details);
response = Response.status(Response.Status.OK).entity(
customerResponse);
}
} else {
response = Response.status(Response.Status.BAD_REQUEST).entity(
customerResponse);
}
}
catch (Exception e) {
LOGGER.error(e.getMessage());
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
}
return response.build();
} }
#Component
#Scope("prototype")
public class CustomerValidations {
public void searchCustomerDetailsValidation(
String name, Integer id,
CustomerResponse customerResponse) {
if (id == null) {
customerResponse.getErrors().add(
new ValidationError("BAD_REQUEST",
""invalid id));
}
if (name== null
|| (name!= null && name
.trim().length() == 0)) {
customerResponse.getErrors().add(
new ValidationError("BAD_REQUEST", "invalid id"));
}
} }
#XmlRootElement
public class CustomerResponse {
private CustomerDetails customerDetails;
private List<ValidationError> errors = new ArrayList<ValidationError>();
//setters and getters }
public class ValidationError {
private String status;
private String message;
public ValidationError() {
}
public ValidationError(String status, String message) {
super();
this.status = status;
this.message = message;
}
//setters and getters }
You're receiving an exception because you have no methods mapped to #Path("/search/{foo}/") or #Path("/search/"), so you should be getting a default 404 response as these paths are not really defined.
I'm not sure why you would want to validate these "missing" request paths though - it looks like this endpoint is intended to be used as a query endpoint so I'd suggest you use #RequestParam/query parameters to more RESTfully describe the search you're attempting. A path of search/{name}/{id} would suggest a specific resource which permanently lives at this URL, though in this case you're querying for customers on this controller.
I would propose you drop the /search path completely and just map query parameters onto the "root" of the Customer controller, so you get something like
#Controller
#Path("/customer")
public class CustomerController extends BaseController implements Customer {
#GET
#Produces({"application/json", "application/xml"})
public Response searchCustomerDetails(
#RequestParam("name") String name,
#RequestParam("id") Integer id) {
// Returns response with list of links to /customer/{id} (below)
}
#GET
#Produces({"application/json", "application/xml"})
#Path("/{id}")
public Response getCustomerDetails(#PathVariable("id") String id) {
// GET for specific Customer
}
}

Categories