I am just learning about Retrofit and Android development. What I would like to do is send a fairly complex JSON object to a server from a website and be able to retrieve it using Retrofit as a Java Object for my Android Application.
So basically something like this,
Website JSON --Ajax Call--> Server --Retrofit--> Android Application (Java Object / Collection)
Which server would be the best to set this up? Also are there any good references on how to accomplish this?
Thank you
With retrofit and android, you only need a couple of things
A java model
public class User {
private String name;
private String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
//Getters and setters
//...
}
A retrofit interface
public interface APIService {
#FormUrlEncoded
#Headers("Accept: application/json")
#POST("register")
Call<AuthRegister> createUser(
#Field("name") String name,
#Field("password") String password
);
}
A callback for retrofit
public class AuthRegister {
#SerializedName("message")
#Expose
private String message;
#SerializedName("errors")
#Expose
private Errors errors;
public String getMessage() {
return message;
}
public Errors getErrors() {
return errors;
}
}
A network client
public class NetworkClient {
public static Retrofit retrofit;
/*
This public static method will return Retrofit client
anywhere in the appplication
*/
public static Retrofit getRetrofitClient() {
//If condition to ensure we don't create multiple retrofit instances in a single application
if (retrofit == null) {
//Defining the Retrofit using Builder
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.level(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
// .addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.MINUTES)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(Config.BASE_URL) //This is the only mandatory call on Builder object.
.client(client)
.addConverterFactory(GsonConverterFactory.create()) // Convertor library used to convert response into POJO
.build();
}
return retrofit;
}
}
The call inside the activity you are to display the response or save the data
private void saveUser(String name, String password){
Retrofit retrofit = NetworkClient.getRetrofitClient();
APIService service = retrofit.create(APIService.class);
Call<AuthRegister> call = service.createUser(name, password);
call.enqueue(new Callback<AuthRegister>() {
#Override
public void onResponse(Call<AuthRegister> call, Response<AuthLogin> response) {
if (response.code() == 200) {
if (response.body().getMessage() != null) {
Toast.makeText(mContext, "Success", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "Could not save user", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(mContext, "Could not save user", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<AuthRegister> call, Throwable t) {
new PrefManager(mContext).clearUser();
Log.e(TAG, t.toString());
Toast.makeText(mContext, "Could not save user", Toast.LENGTH_SHORT).show();
}
});
}
You can use any server as you need. Any complex JSON can be handled by retrofit library. Check the Following link Retrofit android example web services
Related
Iam trying to send an Object from angular to my spring to add a new product. The product has a list of prices and sizes. This is how it looks like in backend:
#Setter
#Getter
public class ProductDto {
private Long id;
private Long secondId;
private String name;
private String description;
private MultipartFile img;
private byte[] returnedImg;
private Long categoryId;
private String categoryName;
private Long restaurantId;
private Long userId;
private List<SizePriceDto> sizePrices;
}
Controller class:
#PostMapping("product/{categoryId}/{restaurantId}/{userId}")
public GeneralResponse addProduct(#PathVariable Long categoryId, #PathVariable Long restaurantId, #PathVariable Long userId, #ModelAttribute ProductDto productDto) {
GeneralResponse response = new GeneralResponse();
try {
return restaurantService.addProduct(categoryId, restaurantId, userId, productDto);
} catch (Exception ex) {
response.setStatus(HttpStatus.BAD_REQUEST);
response.setMessage("Sorry Something Wrong Happened.");
return response;
}
}
Frontend:
this.sizes.controls.forEach((sizeGroup, index) => {
sizesAndPrices.push({
size: sizeGroup.get('size').value,
price: sizeGroup.get('price').value
});
});
formData.append('sizePrices', '[{"size":"30","price":343},{"size":"frefgv","price":343}]');
And this is the error that i get when i submit the form in angular:
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'productDto' on field 'sizePrices': rejected value [[{"size":"34","price":2342},{"size":"f4ef","price":3434}]]; codes [typeMismatch.productDto.sizePrices,typeMismatch.sizePrices,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [productDto.sizePrices,sizePrices]; arguments []; default message [sizePrices]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'sizePrices'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.restaurant.dto.SizePriceDto' for property 'sizePrices[0]': no matching editors or conversion strategy found]]
SizePriceDto Class
#Getter
#Setter
public class SizePriceDto {
private String size;
private Long price;
}
SizePrice Class:
#Entity
#Getter
#Setter
public class SizePrice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String size;
private Long price;
#ManyToOne
#JoinColumn(name = "product_id")
private Product product;
}
My request from angular:
https://i.postimg.cc/zfnXT4mT/image.png
I tried alot of methods but nothing works and i dont have any idea how to fix this error.
I have a hosted service sending messages to the client via SignalR and the client is picking them up and displaying on a razor page. Here is the stripped down version.
This works well except that the .js file is called AFTER the hosted service is instantiated which means the connection is not established in time for the first messages so these are lost.
Is there a way to check the connection using IHubContext before I send anything? Or do I need a more fundamental change to the whole approach?
Javascript (linked to the Index page)
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/logHub").build();
connection.start()
connection.on("ReceiveLogNotification", function (msg) {
console.log("JS: " + msg);
});
Hub class
public class LogHub : Hub
{
}
Hosted Service
public class MsgGenerator : IHostedService
{
private Random _random;
private Microsoft.Extensions.Logging.ILogger _logger;
private Timer? _timer;
private readonly IHubContext<LogHub> _hubContext;
public LogGenerator(ILogger<LogGenerator> logger,
IHubContext<LogHub> hubContext)
{
_logger = logger;
_random = new Random();
_timer = null;
_hubContext = hubContext;
}
public Task StartAsync(CancellationToken cancellationToken)
{
ScheduleNextMessage();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void ScheduleNextMessage()
{
var nextTime = TimeSpan.FromMilliseconds(_random.Next(1000));
_timer = new Timer((state) => { GenerateMessage(); }, null, nextTime, TimeSpan.Zero);
}
private async void GenerateMessage()
{
await _hubContext.Clients.All.SendAsync("ReceiveLogNotification", "test message");
ScheduleNextMessage();
}
I am trying to first use the Retrofit to get data to an course. But I figured out that my program is going to the "onFailure" option.
ERROR MESSAGE:
2020-03-07 18:54:04.499 17756-17756/com.example.apirequest I/MainActivity: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
Basically I'm trying to get firstly only the "launch_year" from API https://api.spacexdata.com/v3/launches/
Can someone help me to figure out the reason I can't get it right?
In grade I added:
implementation 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
My codes are bellow:
MainActivity:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder().
baseUrl(APIService.urlBase)
.addConverterFactory(GsonConverterFactory.create()).
build();
APIService service = retrofit.create(APIService.class);
Call<rocketCatalog> requestModels = service.listCaralog();
requestModels.enqueue(new Callback<rocketCatalog>() {
#Override
public void onResponse(Call<rocketCatalog> call, Response<rocketCatalog> response) {
rocketCatalog catalog = response.body();
for (Rockets rocket : catalog.rocket){
Log.i(TAG,String.format("%s",rocket.getLaunch_date()));
}
}
#Override
public void onFailure(Call<rocketCatalog> call, Throwable t) {
Log.i(TAG,String.format("============= Failure =============="));
}
});
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
}
}
APIService:
public interface APIService {
public static final String urlBase = "https://api.spacexdata.com/v3/";
#GET("launches")
Call<rocketCatalog> listCaralog();
}
rocketCatalog:
public class rocketCatalog {
public List<Rockets> rocket;
public List<Rockets> getRocket() {
return rocket;
}
public void setRocket(List<Rockets> rocket) {
this.rocket = rocket;
}
}
Rockets:
public class Rockets {
String launch_date;
public String getLaunch_date() {
return launch_date;
}
public void setLaunch_date(String launch_date) {
this.launch_date = launch_date;
}
}
Your endpoint returns a List so you need a List type for your response.
public static final String urlBase = "https://api.spacexdata.com/v3/";
#GET("launches")
Call<List<Rockets>> listCaralog();
also make sure the field launch_date in your Rockets Class exists in the response.
with your help I figured it out what was happening.
Indeed I was receiving a List, but I had to re struct, I've made what Mohamed Mohsin suggested and modified my rocketCatalog.java to:
rocketCatalog:
String launch_year;
public String getLaunch_year() {
return launch_year;
}
public void setLaunch_year(String launch_date) {
this.launch_year = launch_date;
}
}
Also, I changed "launch_date" which was wrong to "launch_year"
Thank you so much, again!
Whenever I'm sending JSON from client to server and it tries to convert this JSON into java object. It is giving following exception
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: N/A\n at [Source:
java.io.PushbackInputStream#18f89001; line: 1, column: 1102] (through
reference chain:
Request["fields"]->java.util.ArrayList[0]->Field["value"]); nested
exception is com.fasterxml.jackson.databind.JsonMappingException
My java code:
public class Field implements Serializable {
private String name;
private String value;
public Field(){
}
public Field(String name, String value ) {
this.name = name;
this.value = value;
}
public String getName(){
return name;
}
public String getValue(){
return value;
}
public void setValue(String newValue) throws Exception {
someValidation();//Some validation on value
value = newValue;
}
public void setName(String name) {
this.name = name;
}
}
public class Request implements Serializable{
private String name;
private List<Field> fields;
public Request() {
}
public String getName() {
return viewName;
}
public void setName(String name) {
this.name = name;
}
public List<Field> getFields() {
return fields;
}
public void setField(List<Field> fields) {
this.fields = fields;
}
}
My JSON is :
{
"name" : "name0",
"fields" : [{"name":"abc0", "value":"0"}, {"name":"def0", "value":"0"}]
}
Your code looks fine, I think the problem might be with your request header, which can confuse the server.
I suggest you try to post a simpler JSON to your server, maybe just a string and find out step by step where is your failure point, check your request and maybe simulate one with POSTMAN or something familiar to make sure the client isn't the problem here.
In my index.html page, a variable in the script is hard coded. I want to get it from application.properties file but have no idea how to. It would helpful if anyone could provide me a solution.
I have attached the example. Hope to help.
Application
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class);
}
}
PropertiesController
#RestController
public class PropertiesController {
#Autowired
private UIProperty uiProperty;
#RequestMapping("properties")
public UIProperty getProperties() {
return uiProperty;
}
}
UIProperty
#Component
#ConfigurationProperties(prefix = "ui.label")
public class UIProperty {
private String user;
private String password;
public void setUser(String user) {
this.user = user;
}
public String getUser() {
return user;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
}
application.properties
ui.label.user=user
ui.label.password=password
database.user=
database.password=
I'd create a RestController to expose ConfigurationProperties. But be sure to properly secure it as well as limit in its scope not to disclose confidential data like db access credentials.