I've created a simple Spring Boot app, in which I am trying to consume an API through JSON information. Below you can see the simple code I created using RestTemplate on the Service Class. The problem I am facing is that when I am using the API url below, I am getting the following nested exception.
In case I am using API url with less information, everything works fine. What am I doing wrong?
CONTROLLER CLASS
package com.andrekreou.iot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
public class RestSpringBootController {
private final Service service;
#Autowired
public RestSpringBootController(Service service) {
this.service = service;
}
#GetMapping(path = "/opap")
public List<Object> getWeather(){
return service.getWeather();
}
}
SERVICE CLASS
package com.andrekreou.iot;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.List;
#org.springframework.stereotype.Service
public class Service {
public List<Object> getWeather(){
String url = "https://api.opap.gr/draws/v3.0/5104/last-result-and-active";
RestTemplate restTemplate = new RestTemplate();
Object[] weather = restTemplate.getForObject(url, Object[].class);
return Arrays.asList(weather);
}
}
The problem is in this line of code:
Object[] weather = restTemplate.getForObject(url, Object[].class);
You're mapping this JSON :
{
"last": {
"gameId": 5104,
"drawId": 2446,
"drawTime": 1653850800000,
"status": "results",
"drawBreak": 1800000,
"visualDraw": 2446,
"pricePoints": {
"amount": 0.5
},
"winningNumbers": {
"list": [
1,
9,
19,
22,
33
],
"bonus": [
1
]
},
...
}
Which is not array, it's an object, and that's why you are getting error described in your question.
Change above line of code to:
Object weather = restTemplate.getForObject(url, Object.class);
and it should work fine.
Related
I have seen this issue being addressed in many other posts but none of them has solved my problem. I have a front-end Vue.js application and a spring boot Java application.
I am using the vue-google-oauth to prompt the Google sign in from my front end application to get the auth code, then I wanted to use my backend server to get user details and handle logic there.
On Google Cloud Platform I defined an Authorized redirect URI:
and I am using this very same uri when I am sending my auth code in the front end
import api from "#/assets/js/api";
import AdminNavigation from "./AdminNavigation";
import { mapGetters } from "vuex";
import Axios from "axios";
export default {
name: "Dashboard",
computed: {
...mapGetters(["IsSignedIn"]),
},
data() {
return {
title: "Christopher s' portfolio admin",
appDescription:
"Here you can add contents for the front end portfolio website.",
isInit: false,
};
},
components: {
AdminNavigation,
},
methods: {
signIn: async function () {
try {
const authCode = await this.$gAuth.getAuthCode();
Axios.post("http://localhost:8080/authenticate", {
code: authCode,
redirect_uri: "http://localhost:3000/admin/dashboard",
});
} catch (err) {
console.log(err);
}
},
},
mounted() {
let that = this;
let checkGauthLoad = setInterval(function () {
that.isInit = that.$gAuth.isInit;
if (!this.IsSignedIn) {
that.signIn();
}
if (that.isInit) clearInterval(checkGauthLoad);
}, 1000);
},
};
My backend server receives the auth code and the redirect_uri which is identical to what was defined on Google Cloud Platform.
package com.salay.christophersalayportfolio.controllers;
import com.google.api.client.auth.oauth2.TokenResponseException;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.salay.christophersalayportfolio.general.ConstantVariables;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Controller
public class AdminController {
#CrossOrigin(origins = "http://localhost:3000")
#RequestMapping(value = "/authenticate", method = RequestMethod.POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String authentication(HttpEntity<String> data) throws IOException, ParseException {
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(data.getBody());
String authCode = json.get("code").toString();
String redirect_uri = json.get("redirect_uri").toString();
try {
GoogleTokenResponse response =
new GoogleAuthorizationCodeTokenRequest(new NetHttpTransport(), new JacksonFactory(),
ConstantVariables.GOOGLE_CLIENT_ID,
ConstantVariables.GOOGLE_CLIENT_SECRET,
authCode, redirect_uri).execute();
System.out.println("Access token: " + response.getAccessToken());
} catch (TokenResponseException e) {
if (e.getDetails() != null) {
System.err.println("Error: " + e.getDetails().getError());
if (e.getDetails().getErrorDescription() != null) {
System.err.println(e.getDetails().getErrorDescription());
}
if (e.getDetails().getErrorUri() != null) {
System.err.println(e.getDetails().getErrorUri());
}
} else {
System.err.println(e.getMessage());
}
}
return "";
}
}
But I get the following error:
400 Bad Request redirect_uri_mismatch
I kept looking at a lot of stack overflow questions and no solution worked for me so far... any ideas?
Sounds like you are not sending the OAuth details you think you are. Have you captured HTTPS messages to the Authorization Server from your Spring Boot back end - and can you post details here?
If it helps, this blog post of mine includes some notes on configuring an HTTP proxy in Java.
I want to split a very large string into multiple lines. When I use next line characters, those characters are displayed in Swagger UI without having multiple lines.
Code is as below:
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
#Path("test")
#Api(tags = {"Testing"})
public class TestingService {
#Context
private HttpServletRequest request;
#GET
#Path("testing")
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Testing",
response = String.class)
public String getString(
throws Exception {
return "abcdef,fghijk";
}
}
Input is:
abcdef,fghijk
Current Output:
{
"messages": "abcdef,fghijk"
}
Expected Output:
{
"messages": "abcdef,
fghijk"
}
I have tried \n, \\n, \\\n and \r\n.
Note: I am using Jersey Framework for this REST API & Swagger for UI.
Thanks a lot in advance for the help.
Workaround :
(a) split the string on some basis
(b) create a list of object where object stores the split string.
(c)Send the list to UI as response.
Output will be as below:
{
"messages_list": [
{"message": "abcdef"},
{"message": "fghijk"}
]
}
Note: This was not the actual requirement. This is just workaround to display long & different messages in different lines.
So we're trying to use the OpenAPI generator and so far we've had mixed results.
Steps to reproduce:
Download openapi generator jar: wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator/4.0.3/openapi-generator-4.0.3.jar
Generate springboot server for the petstore example: java -jar openapi-generator-cli-4.0.3.jar generate -g spring -i https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml
You'll end up with controller classes that look like this:
package org.openapitools.api;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.NativeWebRequest;
import java.util.Optional;
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2019-08-06T15:08:49.070+01:00[Europe/London]")
#Controller
#RequestMapping("${openapi.swaggerPetstore.base-path:/v1}")
public class PetsApiController implements PetsApi {
private final NativeWebRequest request;
#org.springframework.beans.factory.annotation.Autowired
public PetsApiController(NativeWebRequest request) {
this.request = request;
}
#Override
public Optional<NativeWebRequest> getRequest() {
return Optional.ofNullable(request);
}
}
/**
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (4.0.3).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.api;
import org.openapitools.model.Error;
import org.openapitools.model.Pet;
import io.swagger.annotations.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2019-08-06T15:08:49.070+01:00[Europe/London]")
#Validated
#Api(value = "pets", description = "the pets API")
public interface PetsApi {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
#ApiOperation(value = "Create a pet", nickname = "createPets", notes = "", tags={ "pets", })
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Null response"),
#ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
#RequestMapping(value = "/pets",
produces = { "application/json" },
method = RequestMethod.POST)
default ResponseEntity<Void> createPets() {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
#ApiOperation(value = "List all pets", nickname = "listPets", notes = "", response = Pet.class, responseContainer = "List", tags={ "pets", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "A paged array of pets", response = Pet.class, responseContainer = "List"),
#ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
#RequestMapping(value = "/pets",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<List<Pet>> listPets(#ApiParam(value = "How many items to return at one time (max 100)") #Valid #RequestParam(value = "limit", required = false) Integer limit) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
ApiUtil.setExampleResponse(request, "application/json", "null");
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
#ApiOperation(value = "Info for a specific pet", nickname = "showPetById", notes = "", response = Pet.class, tags={ "pets", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Expected response to a valid request", response = Pet.class),
#ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
#RequestMapping(value = "/pets/{petId}",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<Pet> showPetById(#ApiParam(value = "The id of the pet to retrieve",required=true) #PathVariable("petId") String petId) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
ApiUtil.setExampleResponse(request, "application/json", "null");
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
So my question is this: everything I can find implies that Spring Controllers are multi-threaded and may handle multiple requests at once. Is the code generator broken? Am I interpreting this completely wrong?
The constructor for PetsApiController gives me pause. If it's being autowired once per request then that implies that there's only one per request?
#org.springframework.beans.factory.annotation.Autowired
public PetsApiController(NativeWebRequest request) {
this.request = request;
}
The swagger code generators are notoriously bad, friend of mine said that the generators have the breadth but not the depth. You can generate skeletons for all sorts of languages and frameworks, but they have severe limitations. For example, try generating a good skeleton from a SwaggerDoc with Page<Something> or other Generics. I would very sadly say that they have almost no utility, and the tools tend to only work reliably the other way around, that is coding first and then generating the SwaggerDoc.
A place I worked at had a great concept I really liked whereby you would design your API first before implementing it, which sounds like you are trying to do. Some IDEs even support generated code, and there are plugins for build tools such as maven gradle etc to generate the code from your yaml.
But in practice I spent days trying to get desirable results from these tools and gave up. I think the real problem is Swagger/OpenAPI is still heavily viewed as documentation tool, not a design tool. I also think that trying to create an all encompassing project generator was setup to fail from the get-go.
I myself tried to customize the moustache templates which the generator used, but generics in Java were a nightmare, and you couldn't get the proper workflow working whereby I would change the SwaggerDoc and then update my code, as my approach was to generate an interface, and then implement that interface, but Annotations weren't inherited so I had to duplicate all the code anway meaning there was no benefit.
I have a sample backend response coming as below:
When I try to map this response into the java object, I am getting following error.
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of com.mc.membersphere.model.MemberSummaryLabel[] out of START_OBJECT token
Seems like the issue with the body tag coming from API. Which has array of objects. I need help, how to handle this body tag arrays value in Java mapping?
Backend API Response:
{
"body": [{
"pcp": "KASSAM, Far",
"er12M": "0",
"ipAdmits12M": "0",
"ipReAdmits12M": "0",
"rx12M": "0",
"pastMedicalHistory": " ",
"erCost12M": "0.0"
}
]
}
Java Program to get the Rest data into the Java objects is as below.
import java.util.Collections;
import java.util.Properties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.mc.membersphere.model.MemberSummaryLabel;
import com.mc.membersphere.utility.PropertyUtil;
public class TestRestclient implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(TestApi.class, args); }
private static Properties prop = PropertyUtil.getProperties();
#Override
public void run(String... args) throws Exception {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
String getMVPSummaryUrl = prop.getProperty("getmvpmembersummary.url");
String url = getMVPSummaryUrl+"/"+"CA";
ResponseEntity<MemberSummaryLabel[]> response = restTemplate.exchange(url, HttpMethod.GET,entity, MemberSummaryLabel[].class);
if(response.getStatusCode()== HttpStatus.OK) {
for(MemberSummaryLabel memberSummaryLabel : response.getBody())
{
System.out.println(memberSummaryLabel.pcp);
}
//System.out.println("Print response" + response);
}
else {
System.out.println("Error");
}
}
}
MemberSummaryLabel is as below.
import com.fasterxml.jackson.annotation.JsonProperty;
public class MemberSummaryLabel {
#JsonProperty("pcp")
public String pcp;
#JsonProperty("er12M")
public Integer er12M;
#JsonProperty("ipAdmits12M")
public Integer ipAdmits12M;
#JsonProperty("ipReAdmits12M")
public Integer ipReAdmits12M;
#JsonProperty("rx12M")
public Integer rx12M;
#JsonProperty("pastMedicalHistory")
public String pastMedicalHistory;
#JsonProperty("erCost12M")
public Double erCost12M;
}
I see, its an issue with your mapping. Your response is in "body" and body contains list of MemberSummaryLabel. So, you need to have one more class as mentioned below,
public class Body{
#JsonProperty("body")
public List<MemberSummaryLabel> memberSummaryLabelList;
}
And your exchange method should return NewClass.
ResponseEntity<Body> response = restTemplate.exchange(url, HttpMethod.GET,entity, Body.class);
And for, iteration use,
for(MemberSummaryLabel memberSummaryLabel : response.getBody().getMemberSummaryLabelList()){
}
For some reason, when I try to print the response using print statement for the response body, the system does not print. please help.
In the following API post, I am using Java, rest-assured, TestNG on Eclipse Neon 3. Using #DataProvider annotation, I am passing multiple params in Post request to see the response of the call. Any help would be truly appreciated.
package com.auto.restassured;
import io.restassured.RestAssured;
import static io.restassured.RestAssured.basic;
import static io.restassured.RestAssured.given;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import io.restassured.response.Response;
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
public class FilePostToVirusTotal {
static String baseURL = "https://www.virustotal.com/vtapi/v2/file/report";
Response myResponse;
#DataProvider(name = "md5hashes")
public String[][] createMd5Hashes() {
return new String[][] {
{"md51", "c1105fb75bc00b5e487f7b26a5be7088"},
{"md52", "213f3287c81d09b095334c9f3151cff8"},
{"md53", "b00c2c458b4cf1eb172e354f54f0fe12"},
{"md54", "32ac9b6b6b7cdbfce179acc5edae98c3"},
{"md55", "510b0b81b85c025d538ed4bad78dc64f"},
};
}
#Test(dataProvider = "md5hashes")
public void md5JsonTest(String apikey, String resource)
{
//Catch API response
myResponse = given().param("text", resource).param("text", "34b937e6e2d28ee6f93a70392d958de8ac4a8dd842e08bbca9bcb0d22f9b9960").when().post(baseURL);
//Print Response
System.out.println(myResponse.getBody().asString());
}
}
You can use built-in log methods e.g. given().log().all() for request and then().log().all() for response
public class Request {
public static void main(String[] args) {
RestAssured.baseURI="http://dummy.restapiexample.com";
given().
//queryParam("key","AIzaSyDIQgAh0B4p0SdyYkyW8tlG-y0yJMfss5Y").
body("{\"name\":\"test111\",\"salary\":\"123\",\"age\":\"23\"}").
when().
post("/api/v1/create").
then().assertThat().statusCode(200).and().contentType(ContentType.JSON).and()
.body("status",equalTo("success")).log().body();
System.out.println("Done");
}
}
Result:
{
"status": "success",
"data": {
"name": "test111",
"salary": "123",
"age": "23",
"id": 65
}
}
Done