I am trying to send POST Request from Android to Spring via Retrofit from past 2 days. I have tried plenty of solutions regarding this, but nothing seems to be working. So, asking here finally to be able to get some help.
So, i am trying to send a simple Object from Android to Spring via retrofit. From android side i have verified the values sent by me and it gives me correct value.(I have debugged it via Android's debugging mode). But on spring side i got null values.
This is my code ->
function foo() -> from which i am sending my request.
private void foo() {
request.setName1("XYZ");
request.setName2("PQR");
Api.upload(request, new Callback<BasicResponse>() {
#Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
Log.d(TAG, "onResponse: Success" + response.toString());
}
#Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
Log.d(TAG, "onResponse: Failure");
}
});
}
}
my upload Function ->
public static void upload(Request request, Callback<BasicResponse> callback) {
uploadRequest api = retrofit.create(uploadRequest.class);
Call<BasicResponse> call = uploadRequest.uploadCall(POJO);
call.enqueue(callback);
}
This is my UploadRequest Interface ->
public interface uploadRequest{
#POST(UPLOAD_REQUEST)
Call<BasicResponse> uploadCall(#Body POJO pojo);
}
This is My POJO Class
public class POJO {
private String Name1;
private String Name2;
public String getName1() {
return Name1;
}
public void setName1(String name1) {
Name1 = name1;
}
public String getName2() {
return Name2;
}
public void setName2(String name2) {
Name2 = name2;
}
}
And this is my Spring Controller Method
#RequestMapping(value = "/UploadRequest",method = RequestMethod.POST,consumes = "application/json", produces = "application/json")
#ResponseBody
public void UploadImage(#RequestBody POJO pojo,HttpServletRequest request) {
if(pojo!=null){
System.out.println("pojo is not null");
System.out.println(pojo.getName1());
}
else{
System.out.println("null");
}
}
I am getting pojo is not null and inside the pojo.getName1(), the value prints is null.
Edit : Adding BasicResponse Class.
public class BasicResponse {
private boolean success = Boolean.TRUE;
private ErrorCode errorCode;
private String response;
private Integer totalCount;
private String callingUserId;
public BasicResponse() {
super();
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public ErrorCode getErrorCode() {
return errorCode;
}
public void setErrorCode(ErrorCode errorCode) {
this.errorCode = errorCode;
this.success = false;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public void setResponse(Object response) {
if (response != null) {
this.response = GsonUtils.toGson(response);
}
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public String getCallingUserId() {
return callingUserId;
}
public void setCallingUserId(String callingUserId) {
this.callingUserId = callingUserId;
}
}
Compare the response and your POJO class. The instance variables of your POJO class must be the same as in response. In your case Name1 and Name2. If they are name1, name2 in the response (which means if they do not start with capital letters, etc.), or different, it gives you NullPointerException.
Most likely Name1 and Name2 have different names in json than in your POJO and Jackson, with is used under the hood when you annotate your parameters with #RequestBody can not figure them out, so you get null values. Check out your json.
Related
I am new to android app development with java and I am trying to fetch data from an external api .
However I get the error : Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $.The API returns a result in the form of :
[ {} ,{} , ... ]
where I have defined every object as a single result and in my API interface I want a list of these results .
Each object is in the form of :
{
area: "athens",
areaid: 1002,
dailydose1: 276,
dailydose2: 305,
daydiff: -49,
daytotal: 581,
referencedate: "2021-05-27T00:00:00",
totaldistinctpersons: 25446,
totaldose1: 25446,
totaldose2: 12098,
totalvaccinations: 36960
}
My code :
CovidApi.java
public interface CovidApi {
#GET("/")
Call<List<CovidSingleResult>> getCovidData();
}
CovidSingleResult.java
public class CovidSingleResult {
private int totalvaccinations;
private int daytotal;
private String referencedate;
private int totaldose1;
private int totaldose2;
private String area;
private int areaid;
private int dailydose1;
private int dailydose2;
private int daydiff;
public String getArea() {
return area;
}
public int getDailydose1() {
return dailydose1;
}
public int getDaytotal() {
return daytotal;
}
public int getDailydose2() {
return dailydose2;
}
public String getReferencedate() {
return referencedate;
}
public int getAreaid() {
return areaid;
}
public int getTotaldose1() {
return totaldose1;
}
public int getTotaldose2() {
return totaldose2;
}
public int getDaydiff() {
return daydiff;
}
public int getTotalvaccinations() {
return totalvaccinations;
}
}
Fragment.java where I call the api url
resultText = root.findViewById(R.id.response); //text to set api result
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://data.gov.gr/api/v1/query/mdg_emvolio/date_from="+binding.inputFrom.getText()+"&date_to="+binding.inputTo.getText()+"/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
CovidApi covidApi = retrofit.create(CovidApi.class);
Call<List<CovidSingleResult>> call = covidApi.getCovidData();
call.enqueue(new Callback<List<CovidSingleResult>>() {
#Override
public void onResponse(Call<List<CovidSingleResult>> call, retrofit2.Response<List<CovidSingleResult>> response) {
if(!response.isSuccessful()){
resultText.setText("Code" + response.code());
return;
}else{
List<CovidSingleResult> covidSingleResults = response.body();
for(CovidSingleResult p : covidSingleResults){
String content = "";
content += "total vaccinations :" + p.getTotalvaccinations();
resultText.append(content);
}
}
}
#Override
public void onFailure(Call<List<CovidSingleResult>> call, Throwable t) {
resultText.setText(t.getMessage());
}
});
I would appreciate your help
Error say`s that your response is object and your model in java is list
please replace bellow code in api interface:
Call<CovidSingleResult> getCovidData();
here my module
public class SPSOnHand {
#SerializedName("ITEM_CODE")
#Expose
private String ITEM_CODE;
#SerializedName("ITEM_DESCRIPTION")
#Expose
private String ITEM_DESCRIPTION;
public String getITEM_CODE() {
return ITEM_CODE;
}
public void setITEM_CODE(String item_code) {
ITEM_CODE = item_code;
}
public String getITEM_DESCRIPTION() {
return ITEM_DESCRIPTION;
}
public void setITEM_DESCRIPTION(String item_description) {
ITEM_DESCRIPTION = item_description;
}
here my rest service class
public class RestService {
private static final String URL = "http://localhost:58364/";
private retrofit.RestAdapter restAdapter;
//private InstituteService apiService;
private SPInventoryService apiService;
public RestService()
{
restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(URL)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.build();
apiService = restAdapter.create(SPInventoryService.class);
}
public SPInventoryService getService()
{
return apiService;
}
here my service
#GET("/api/SPSOnHand/itemcode/{item_code}")
public void getItemCode(#Path("item_code") String itemcode,
Callback<ArrayList<SPSOnHand>>callback);
here my main activity class
String itemcode;
Intent intent = getIntent();
itemcode =intent.getStringExtra("itemcode");
restService.getService().getItemCode(itemcode, new Callback<ArrayList<SPSOnHand>>() {
#Override
public void success(ArrayList<SPSOnHand> spsOnHands, Response response) {
item_description.setText(String.valueOf(spsOnHands.get(0)));
}
#Override
public void failure(RetrofitError error) {
Toast.makeText(AirJet.this, error.getMessage().toString(),
Toast.LENGTH_LONG).show();
}
});
I want that item_description to contain a word like this
"ID": 167,
"ITEM_CODE": "3020240002",
"ITEM_DESCRIPTION": "CONTROLLER LME22.331c2 (CONTROL BOX)"
thanks in advance, I'm still a newbie in retrofit
Because of String.valueOf(spsOnHands.get(0)) whole item of list converted to String so that's why you are getting wrong format of text.
Use below code for set text in textview:
int id = 0+1;
String itemCode = arrayList.get(0).getITEM_CODE();
String itemDescription = arrayList.get(0).getITEM_CODE();
item_description.setText("\"ID\":"+ id+" \n\"ITEM_CODE\":" + itemCode+" \n\"ITEM_DESCRIPTION\":"+itemDescription);
You can change format of text in textview according to your requirement.
In your case, you receive ArrayList. And if you want to get the item from List then you have to pass index and get value List, like below. It will give you ITEM_DESCRIPTION for the 0th index of your List.
restService.getService().getItemCode(itemcode, new Callback<ArrayList<SPSOnHand>>() {
#Override
public void success(ArrayList<SPSOnHand> spsOnHands, Response response) {
item_description.setText(String.valueOf(spsOnHands.get(0).ITEM_DESCRIPTION));
}
#Override
public void failure(RetrofitError error) {
Toast.makeText(AirJet.this, error.getMessage().toString(),
Toast.LENGTH_LONG).show();
}
});
I am creating my first Restful web service with Embedded Jetty with authentication and authorization and I have a filter in which I would like to inject a user object (Employee) which then I can retrieve in a service bean using ResteasyProviderFactory.pushContext() the #Context annotation, but whatever I try the object always is null. I would appreciate any kind of help.
#PreMatching
public class AuthenticationHandler implements ContainerRequestFilter {
#Inject private PxCredentialService credentialService;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Response faultresponse = createFaultResponse();
String authorization = requestContext.getHeaderString("Authorization");
String[] parts = authorization.split(" ");
if (parts.length != 2 || !"Basic".equals(parts[0])) {
requestContext.abortWith(createFaultResponse());
return;
}
String decodedValue = null;
try {
decodedValue = new String(Base64Utility.decode(parts[1]));
} catch (Base64Exception ex) {
requestContext.abortWith(createFaultResponse());
return;
}
String[] namePassword = decodedValue.split(":");
Employee emp = credentialService.getCredentialsByLoginAndPass(namePassword[0], namePassword[1], true);
if ( emp != null) {
ResteasyProviderFactory.pushContext(Employee.class, emp);
} else {
throw new NullPointerException("False Login");//requestContext.abortWith(Response.status(401).build());
}
}
#Path( "/people" )
public class PeopleRestService implements credentials {
#Inject private PeopleService peopleService;
#Inject private GenericUserRightsUtil genericUserRightsUtil;
#Produces( { "application/json" } )
#GET
public Collection<Person> getPeople(#Context Employee emp) {
Employee emp = (Employee)crc.getProperty("Employee");
return peopleService.getPeople( page, 5 );
}
}
On my understanding, you want an easy way to identify the user who is performing the request in your resource methods. Have you ever considered setting a SecurityContext with a Principal for the request?
In your filter, if the user credentials as valid, do the following
final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return username;
}
};
}
#Override
public boolean isUserInRole(String role) {
return true;
}
#Override
public boolean isSecure() {
return currentSecurityContext.isSecure();
}
#Override
public String getAuthenticationScheme() {
return "Basic";
}
});
Your resource method will be like:
#GET
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response foo(#PathParam("id") Long id,
#Context SecurityContext securityContext) {
...
}
To get the Principal, use:
Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
I am trying to map JSON response as below:
{
object: {
id: 1
name: "my name"
email: "username#mail.com"
username: "username"
password: "password"
mobile: "##########"
fbAccessToken: "----------"
img: null
}
errorMessage: ""
successMessage: ""
technicalErrorMessage: ""
error: false
}
so I wrote this method:
private <T> ResponseEntity<T> processedRequest(HttpRequestBase requestBase, Class<T> tClass) throws IOException {
HttpResponse response = httpClient.execute(requestBase);
HttpEntity entity = response.getEntity();
Reader reader = new InputStreamReader(entity.getContent());
Type type = new TypeToken<ResponseEntity<T>>() {}.getType();
ResponseEntity<T> responseEntity = gson.fromJson(reader, type);
return responseEntity;
}
based on ResponseEntity class:
public class ResponseEntity<T> {
private T object;
private boolean isError;
private String errorMessage;
private String successMessage;
private String technicalErrorMessage;
public ResponseEntity() {
setSuccessMessage("");
setError(false);
setErrorMessage("");
setTechnicalErrorMessage("");
}
public T getObject() {
return object;
}
public void setObject(T object) {
this.object = object;
}
public boolean isError() {
return isError;
}
public void setError(boolean error) {
this.isError = error;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getTechnicalErrorMessage() {
return technicalErrorMessage;
}
public void setTechnicalErrorMessage(String technicalErrorMessage) {
this.technicalErrorMessage = technicalErrorMessage;
}
public String getSuccessMessage() {
return successMessage;
}
public void setSuccessMessage(String successMessage) {
this.successMessage = successMessage;
}}
but I am getting result as ResponseEntity<LinkedTreeMap> and the object is map of (Key->Value) not the actual mapped object that send by the Type ResponseEntity<T>.
The image below is what appeared in the debugger:
How GSON should understand, which class it should use in place of T and what is the "actual mapped object that send by the Type" to fill it's fields? We have type erasure for generics in Java, so no way in runtime to understand what it T. No way, so gson just uses generic Map<String, String>().
Take a look at responses to this question, it's the same situation.
EDIT: The error was in the client not the server. The response body was getting written, but the client was not reading it on a 400 response.
I have a custom message converter to produce text/csv, application/csv from an ErrorResponse object. It works as expected when the ErrorResponse is returned directly from a #RequestMapping annotated method, but returns no response body when ErrorResponse is return from an #ExceptionHandler annotated method in a #ControllerAdvice object. I have verified that the message converter writerInternal method is being called and is writing to the response body, but is never makes it back to the client.
ErrorResponse:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="response")
public class ErrorResponse {
private String statusCode;
private String userMessage;
private String developerMessage;
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(final String statusCode) {
this.statusCode = statusCode;
}
public String getUserMessage() {
return userMessage;
}
public void setUserMessage(final String userMessage) {
this.userMessage = userMessage;
}
public String getDeveloperMessage() {
return developerMessage;
}
public void setDeveloperMessage(final String developerMessage) {
this.developerMessage = developerMessage;
}
public ErrorResponse() {
super();
}
public ErrorResponse(final String statusCode, final String userMessage, final String developerMessage) {
super();
this.statusCode = statusCode;
this.userMessage = userMessage;
this.developerMessage = developerMessage;
}
}
MessageConverter:
public class ErrorResponseCsvMessageConverter extends AbstractHttpMessageConverter<ErrorResponse> {
public ErrorResponseCsvMessageConverter() {
super(new MediaType("application", "csv", Charset.forName("UTF-8")),
new MediaType("text", "csv", Charset.forName("UTF-8")),
MediaType.TEXT_PLAIN);
}
#Override
protected ErrorResponse readInternal(final Class<? extends ErrorResponse> clazz, final HttpInputMessage httpInputMessage)
throws IOException, HttpMessageNotReadableException {
// not supported
return null;
}
#Override
protected boolean supports(final Class<?> clazz) {
return ErrorResponse.class.isAssignableFrom(clazz);
}
#Override
protected void writeInternal(final ErrorResponse errorResponse, final HttpOutputMessage httpOutputMessage)
throws IOException, HttpMessageNotWritableException {
System.out.println(errorResponse);
try(CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(httpOutputMessage.getBody(), "UTF-8"))) {
csvWriter.writeNext(new String[] { "statusCode", "userMessage", "developerMessage" });
csvWriter.writeNext(new String[] {
errorResponse.getStatusCode(),
errorResponse.getUserMessage(),
errorResponse.getDeveloperMessage() });
}
}
}
Controller Advice:
...
#ExceptionHandler(MissingServletRequestParameterException.class)
#ResponseBody()
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleMissingParamterException(final HttpServletRequest request, final HttpServletResponse httpServletResponse, final MissingServletRequestParameterException e) {
LOG.warn("Bad Request:" +
request.getRequestURI() +
((request.getQueryString()==null) ? "" : "?" + request.getQueryString()));
return new ErrorResponse(
"400",
"There was an error with the request.",
"Required parameter '" + e.getParameterName() + "' is missing.");
}
...
I think the message is being written but not flushed...
So your converter may be missing something like:
outputMessage.getBody().flush();
Maybe even use Spring's AbstractHttpMessageConverter ?