How to deal with the onFailrure method throwing a JsonSyntaxException? - gson

I'm having trouble understading Retrofi2 and the callback. Right now I'm receiving a JWT response from the back-end.
The callback send me right in to the onFailure() Method and cuts off me of from the response body and I'm left with a JsonSyntaxException:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
I searched on the internet for a solution, but I'm confused. Some say the Gson is expecting the JSON string with openings brace. Or something about a JSON-array not being a JSON-object.
How can I check this and can I modify a presumably broken Json string?
Right now the back end tells me the response is sends-off without failure.
I'm also using a HttpLoggingInterceptor which seems to give me the JWT string just fine. I'm lost and don't know what to do next.
import android.content.Context;
import android.util.Base64;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.ssl.app.rest.User;
import com.ssl.app.rest.UserClient;
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class LoginActivity {
protected static void login(Context context) {
String username = "jack";
String password = "9375";
String base = username + ":" +password;
String authHeader = "Basic " + Base64.encodeToString(base.getBytes(), Base64.NO_WRAP);
// Interceptor
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(httpLoggingInterceptor).build();
// Setup request
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://10.0.2.2:8080/")
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient);
Retrofit retrofit = builder.build();
UserClient userClient = retrofit.create(UserClient.class);
// Request
Call<User> loginResponseCall = userClient.getUser(authHeader);
// Response
loginResponseCall.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if(response.isSuccessful()){
Toast.makeText(context,"Login Successful :) \n"+response.body(), Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
if (t instanceof IOException) {
Toast.makeText(context,"this is an actual network failure :( \n"+t, Toast.LENGTH_LONG).show();
System.out.println("this is an actual network failure\n" +t);
} else if (t instanceof JsonSyntaxException) {
Toast.makeText(context,"Gson fails to read a malformed JSON element \n"+t, Toast.LENGTH_LONG).show();
System.out.println("Gson fails to read a malformed JSON element\n" +t);
} else {
Toast.makeText(context,"conversion issue! big problem :( \n"+t, Toast.LENGTH_LONG).show();
System.out.println("conversion issue! big problem :(\n" +t);
}
}
});
}
}
public class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Error
D/OkHttp: --> POST http://10.0.2.2:8080/token
Content-Length: 0
Authorization: Basic am9fbjoxMbM0NQ==
--> END POST (0-byte body)
D/OkHttp: <-- 200 http://10.0.2.2:8080/token (69ms)
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 466
D/OkHttp: Date: Mon, 05 Dec 2022 10:23:52 GMT
Keep-Alive: timeout=60
Connection: keep-alive
eyJhbGciOiJSUzI1NiJ3.eyJpc3MiOiJzZWxmIiwic3ViIjoiam9obiIsImV4cCI6MTY3MDIzOTQzNiwiaWF0IjoxNjcwMjM1ODMyLCJzY29wZSI3InJlYWQifQ.eS8Z1Q_rYsofMDTdDhMpVxxhqElCXzHUCYEjr5_t4EC_ZFO1x1Axu045Bcy8I3zhVJUVY4borFw0qBM6GuOgy_j7vOpgeTjq_JksqDHY2jd8Yif3AVHbeBb_eJV-P2iKS34kawNEI3591A7-ZqoDYveCBKqpMU1MkWL2vfWkkcat_8EroeKQCcLRyCYhkTb9Ev2_rH8Zp8wWaNs6pPkfysV0OGJX171fKGdB5pZ5hZsjwzxDMS8jLFANNGz6rIT4jiaz0apiARF86SjPFOHKM4GNrklfa2LZEr_3xyksqd0InJsilHUWr3r6ahXjoaTO3KGHWV3dcg3BidLe66YzfQ
<-- END HTTP (466-byte body)
I/System.out: Gson fails to read a malformed JSON element
I/System.out: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
import retrofit2.Call;
import retrofit2.http.Header;
import retrofit2.http.POST;
public interface UserClient {
#POST("token")
Call<User> getUser(#Header("Authorization") String autHeader);
}

please check it your api response is array of object or object of array according to you can arrange the pojo class

Related

Webflux #RequestBody return 400 BAD_REQUEST

When developing a controller webflux on post request in the parameters of the controller, I pass the request body using the annotation, but in webtestclient method return 400 BAD_REQUEST
My DTO:
#AllArgsConstructor
#Data
public class VisitRequest {
private String description;
public static Visit createEntityFromDto(Long customerId, Long deviceId, VisitRequest visitRequest) {
return new Visit(null, customerId, deviceId, visitRequest.getDescription(),
null);
}
}
My Controller:
#RestController
#RequestMapping(value = VisitController.REST_URL)
#AllArgsConstructor
#Slf4j
public class VisitController {
static final String REST_URL = "/api/customers/{customerId}/devices/{deviceId}";
private final VisitService visitService;
//POST http://visits-service/api/customers/{customerId}/devices/{deviceId}/visits
#PostMapping(value = "/visits", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Visit> save(#PathVariable("customerId") #Validated long customerId,
#PathVariable("deviceId") #Validated long deviceId,
#RequestBody VisitRequest visitRequest) {
log.info("save {} for customer_id: {}, device_id {}", visitRequest, customerId, deviceId);
return visitService.save(createEntityFromDto(customerId, deviceId, visitRequest))
.switchIfEmpty(error(new RuntimeException("Bad request for save visit:" + visitRequest)));
}
}
My Test class with WebTestClient:
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = VisitApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class VisitControllerTest {
#LocalServerPort
private int port;
#Test
void save() throws Exception {
WebTestClient
.bindToServer()
.baseUrl("http://localhost:" + port)
.build()
.post()
.uri("/api/customers/" + ONE.getCustomerId()
+ "/devices/" + ONE.getDeviceId() + "/visits")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.bodyValue(new VisitRequest("test"))
.exchange()
.expectStatus().isCreated()
.expectHeader().valueEquals("Content-Type", MediaTypes.ALPS_JSON_VALUE);
}
}
When I run the test, WebTestClient return:
> POST http://localhost:63028/api/customers/10000/devices/20000/visits
> WebTestClient-Request-Id: [1]
> Content-Type: [application/json]
> Accept: [application/json]
> Content-Length: [22]
{"description":"test"}
< 400 BAD_REQUEST Bad Request
< Content-Type: [application/json]
< Content-Length: [169]
{"timestamp":"2020-09-27T19:28:09.987+00:00","path":"/api/customers/10000/devices/20000/visits","status":400,"error":"Bad Request","message":"","requestId":"42d54765-1"}
When I put a breakpoint on the logging line in the save method controller in the IntelliJ IDEA, and when I run the test in debug, the controller returns a bad request before reaching the breakpoint
Please help me find the cause of the error

Spring throwing 403 exception on POST request but POSTMAN request working

I am trying to POST some data to rest api, When I send the request to API using SPRING REST I get the 403 exception.
I have tried adding user-agent header as suggested by other answers but nothing has worked for me so far. I also checked that access key when using POSTMAN and when calling the service is same. Any advice would be helpful;
The wrapper class to create the body of POST request
public class ApiRequest implements Serializable {
private static final long serialVersionUID = 3729607216939594972L;
#JsonProperty("id")
List<Integer> id;
#JsonProperty("sdate")
String sdate;
#JsonProperty("edate")
String edate;
#JsonProperty("fields")
List<String> fields;
public ApiRequest(List<Integer> id, String sdate, String edate, List<String> fields){
this.id=id;
this.sdate=sdate;
this.edate=edate;
this.fields=fields;
}
public void setEdate(String edate) {
this.edate = edate;
}
public void setSdate(String sdate){
this.sdate=sdate;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
public void setId(List<Integer> id) {
this.id = id;
}
public String getEdate() {
return edate;
}
public String getSdate() {
return sdate;
}
public List<String> getFields() {
return fields;
}
public List<Integer> getId() {
return id;
}
#Override
public String toString() {
return "ApiRequest{" +
"id=" + id +
", sdate=" + sdate +
", edate=" + edate +
", fields=" + fields+
'}';
}
}
Code to call the api
private HttpHeaders getRequestHeaders() {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
requestHeaders.setAccept(Arrays.asList(MediaType.ALL));
requestHeaders.set("user-agent","Some User Agent);
requestHeaders.set("access_token", "ACCESS_TOKEN");
return requestHeaders;
}
ApiRequest request=new ApiRequest(Arrays.asList(10),DateUtil.today().toString(),DateUtil.today().plusDays(10).toString(),Arrays.asList("ALL"));
String response=post("RANDOM_URL",null,null,request,getRequestHeaders(),String.class,"");
Post super method:
public <T> T post(String baseUrl, String url, String query, Object body, HttpHeaders requestHeaders, Class<T> responseClassType, String logTag) {
// In this method body is converted to Json String and called the restExchange
If you are sure that with Postman you are getting correct results then you can enable debug logs for the underlying httpclient ( if apache http client is the underlying http library) by setting logging.level.org.apache.http=DEBUG. This will print all the request details like url, headers etc by which you can compare with what you are sending with Postman. If the client library is something different then you may need to write an interceptor to capture all the request details as explained here.

Postman 304 Not Modified error, spring rest api

Postman image body
Postman image header
I trying to send POST method to Spring rest api but it always shows me a 304 error. Did anyone know what could cause this problem?
Thanks
Client Controller.java(spring controller)
/* change client ip and client port. if change successful, save the settings to the dabase */
#RequestMapping(value = "/clients/setConfiguration/{id}", method = RequestMethod.POST)
public ResponseEntity<?> setConfiguration(#PathVariable Long id, #RequestBody ClientConfigDTO clientConfigDTO) {
System.out.println("POST: ClientController.setConfiguration for client " + id);
HttpStatus result = clientService.setConfiguration(id, clientConfigDTO);
return new ResponseEntity<>(result);
}
ClientConfigDto.java
package at.fhwn.ma.serverapp.dto;
public class ClientConfigDTO {
private String ip;
private String port;
public ClientConfigDTO() {}
public ClientConfigDTO(String ip, String port) {
this.ip=ip;
this.port=port;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}

Jersey - Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin'

Error Screenshot :
The following is my API class in which I have written code for #OPTIONS method.
#OPTIONS
public Response OptionsFirstRequst(){
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "*")
.header("Access-Control-Allow-Headers", "*").build();
}
I have created a class called Response Builder using which for each request I send response.
The following is the code for the Response Builder class:
public class ResponseBuilder {
public int status;
public HashMap data;
public String error;
public static Response ok(int Status_code, HashMap<String, String> data, String Response_error) {
if (data == null) {
data = new HashMap();
}
ResponseBuilder response = new ResponseBuilder();
response.status = Status_code;
response.data = data;
response.error = Response_error;
return Response.status(Status_code).entity(response)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "*")
.header("Access-Control-Allow-Headers", "*").build();
}
public static Response error(int Status_code, HashMap<String, String> data, String Response_error) {
if (data == null) {
data = new HashMap();
}
ResponseBuilder response = new ResponseBuilder();
response.status = Status_code;
response.data = data;
response.error = Response_error;
response.data = new HashMap();
return Response.status(Status_code).entity(response)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "*")
.header("Access-Control-Allow-Headers", "*").build();
}
}
I also have a Request filter which validates token for each request , except for login.
I am able to login , generate token and give it back to the browser.
But after login if I click on profile.
I get response as 200 (as shown in the dev-tools network of the browser) , but I am not getting any data/correct response.
And I am getting the following error.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
To check and add CORS headers, a common solution is to use a javax.ws.rs.container.ContainerResponseFilter. Here an example, where the allowed origins are configured in a class ApplicationConfig.accessControlAllowedOrigins:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class ResponseCorsFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
MultivaluedMap<String, Object> responseHeaders = responseContext.getHeaders();
String origin = requestContext.getHeaderString("Origin");
if (null != origin &&
(ApplicationConfig.accessControlAllowedOrigins.contains(origin) ||
ApplicationConfig.accessControlAllowedOrigins.contains("*"))) {
responseHeaders.putSingle("Access-Control-Allow-Origin", origin);
responseHeaders.putSingle("Access-Control-Allow-Methods",
"GET, POST, OPTIONS, PUT, DELETE, HEAD");
String reqHead = requestContext.getHeaderString(
"Access-Control-Request-Headers");
if (null != reqHead && !reqHead.equals("")) {
responseHeaders.putSingle("Access-Control-Allow-Headers", reqHead);
}
}
}
}

Spring RESTFul Client – RestTemplate. Have weird somthing,Please let me know

I'm using ResTemplate to post User object in CLient. But When i use method postForObject then occur Unresolved compilation problem The method postForObject(URI, Object, Class<T>) in the type RestTemplate is not applicable for the arguments (URL, User, Class<User>). I really don't understand ???
Here file RestClientTest.java`
public class RestClientTest {
public static void main(String[] args) throws IOException{
// System.out.println("Rest Response" + loadUser("quypham"));
// URL url = new URL("http://localhost:8080/rest/user/create");
// rt.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// rt.getMessageConverters().add(new StringHttpMessageConverter());
// Map<String,String> vars = new HashMap<String,String>();
RestTemplate rt = new RestTemplate();
User user = new User();
user.setUserName("datpham");
user.setPassWord("12345");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR,1960);
user.setBirthDay(calendar.getTime());
user.setAge(12);
String uri = new String("http://localhost:8080/rest/user/create");
User returns = rt.postForObject(uri, user,User.class);
// createUser(user);
System.out.println("Rest Response" + loadUser("datpham"));
}
Here file UserRestServiceController
#Controller
public class UserRestServiceController {
#Autowired
public UserDao userDao;
#RequestMapping(value = "/rest/user/create",method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public void addUser(#RequestBody User user){
userDao.save(user);
}
I have edited String uri but encounter new following error:
Mar 29, 2016 1:57:43 PM org.springframework.web.client.RestTemplate
handleResponseError WARNING: POST request for
"http://localhost:8080/rest/user/create" resulted in 400 (Bad
Request); invoking error handler Exception in thread "main"
org.springframework.web.client.HttpClientErrorException: 400 Bad
Request at
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at
org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:588)
at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:546)
at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502)
at
org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:330)
at
edu.java.spring.service.client.RestClientTest.main(RestClientTest.java:45)
Here User.java
#Entity
#Table(name = "brotheruser",uniqueConstraints={#UniqueConstraint(columnNames="username")})
#JsonIgnoreProperties(ignoreUnknown = true)
public class User {
// #Enumerated(EnumType.STRING)
// #Column(name = "gender", nullable = false)
//
// public Gender getGender() {
// return gender;
// }
// public void setGender(Gender gender) {
// this.gender = gender;
// }
#Id
#Column(name = "username", unique = true, nullable = false)
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
#Column(name = "password", nullable = false)
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
#JsonSerialize(using = DateSerializer.class)
// #JsonDeserialize(using = DateDeserializer.class)
#Column(name = "birthday", nullable = false)
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
#Column(name="age", nullable = false)
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
private String userName;
private String passWord;
private Date birthDay;
private Integer age;
// private Gender gender;
}
Ok. I can't give the correct answer right now. My guess is thts in your User class.
The thing is with Http 400 errors, that you have to catch a detailed error message on the server side -to know what is exactly! going wrong. Define a globale exception handler in spring with #ControllerAdvide (and this will help you inwith everything you programm). It's not complicated! just copy & paste the two classes into your project and make sure they get executed, and you will get a detailed outup of the error on the console or the http log file if you don't have access to console
hope this helps ..
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.*;
import java.util.ArrayList;
import java.util.List;
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return super.handleNoHandlerFoundException(ex, headers, status, request);
}
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
return super.handleExceptionInternal(ex, body, headers, status, request);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
List<ObjectError> globalErrors = ex.getBindingResult().getGlobalErrors();
List<String> errors = new ArrayList<>(fieldErrors.size() + globalErrors.size());
String error;
for (FieldError fieldError : fieldErrors) {
error = fieldError.getField() + ", " + fieldError.getDefaultMessage();
errors.add(error);
}
for (ObjectError objectError : globalErrors) {
error = objectError.getObjectName() + ", " + objectError.getDefaultMessage();
errors.add(error);
}
RestResponseErrorMessage errorMessage = new RestResponseErrorMessage(errors);
return new ResponseEntity(errorMessage, headers, status);
}
#Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String unsupported = "Unsupported content type: " + ex.getContentType();
String supported = "Supported content types: " + MediaType.toString(ex.getSupportedMediaTypes());
RestResponseErrorMessage errorMessage = new RestResponseErrorMessage(unsupported, supported);
return new ResponseEntity(errorMessage, headers, status);
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Throwable mostSpecificCause = ex.getMostSpecificCause();
RestResponseErrorMessage errorMessage;
if (mostSpecificCause != null) {
String exceptionName = mostSpecificCause.getClass().getName();
String message = mostSpecificCause.getMessage();
errorMessage = new RestResponseErrorMessage(exceptionName, message);
} else {
errorMessage = new RestResponseErrorMessage(ex.getMessage());
}
return new ResponseEntity(errorMessage, headers, status);
}
}
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Created by pk on 08.03.2016.
*/
#XmlRootElement
public class RestResponseErrorMessage {
private List<String> errors;
public RestResponseErrorMessage() {
}
public RestResponseErrorMessage(List<String> errors) {
this.errors = errors;
}
public RestResponseErrorMessage(String error) {
this(Collections.singletonList(error));
}
public RestResponseErrorMessage(String ... errors) {
this(Arrays.asList(errors));
}
public List<String> getErrors() {
return errors;
}
public void setErrors(List<String> errors) {
this.errors = errors;
}
}
I think problem is with URL object. Try creating URI object or simple string like String url = "http://localhost:8080/rest/user/create";
RestTemplate supports URI or String as first parameter for for postForObject method

Resources