我正在使用Retrofit 2,我正在尝试从使用Python制作的Web服务请求一些数据 .
实际上它会抛出一个“java.lang.IllegalStateException”,但是当我调用GET方法时,API返回代码200,我也可以看到一个用于调试响应JSON的打印版本 . 问题是Call方法,因为它始终执行OnFailure .
请帮忙!
这是我的(简单)Python Web服务,api-prueba:
from flask import Flask, jsonify
import json
import mysql.connector
import collections
app = Flask(__name__)
cnx = mysql.connector.connect(user="root", password="", host="localhost", database="gym")
@app.route('/gym/api/v1.0/clases', methods=['GET'])
def getClases():
sql = "SELECT id, nombre, descripcion FROM clases"
cursor = cnx.cursor()
cursor.execute(sql)
entradas = cursor.fetchall()
# Convertir la query en un array de entradas
objects_list = []
for row in entradas:
objects_list.append(row)
j = json.dumps(objects_list)
return j
@app.route('/')
def index():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
这是我的ApiClient:
public class ApiClient {
private static ApiService REST_CLIENT;
private static final String API_URL = "http://10.0.2.2:5000/gym/api/v1.0/"; //Change according to your API path.
static {
setupRestClient();
}
private ApiClient() {
}
public static ApiService get() {
return REST_CLIENT;
}
private static void setupRestClient() {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
//Uncomment these lines below to start logging each request.
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
REST_CLIENT = retrofit.create(ApiService.class);
}
}
我的API interace,ApiService:
public interface ApiService {
@GET("clases")
Call> getClases();
}
这是查询API并获取数据的片段,ClaseFragment:
public class ClaseFragment extends Fragment implements View.OnClickListener {
private List clases;
private ListView listView;
private Button btnRefresh;
public ClaseFragment() {
// Required empty public constructor
}
public static ClaseFragment newInstance() {
ClaseFragment fragment = new ClaseFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_clase, container, false);
listView = (ListView) root.findViewById(R.id.claseList);
btnRefresh = (Button) root.findViewById(R.id.btnRefresh);
btnRefresh.setOnClickListener(this);
getListaClases();
return root;
}
@Override
public void onClick(View v) {
//do what you want to do when button is clicked
switch (v.getId()) {
case R.id.btnRefresh:
Log.d(getTag(), "hello");
getListaClases();
break;
default:
break;
}
}
private void getListaClases() {
final ProgressDialog loading = ProgressDialog.show(getActivity(),
getContext().getString(R.string.loading_title),
getContext().getString(R.string.loading_please_wait),
false,
false);
Call> call = ApiClient.get().getClases();
call.enqueue(new Callback>() {
@Override
public void onFailure(Call> call, Throwable t) {
Log.d("ApiService", "Error Occured: " + t.getMessage());
loading.dismiss();
}
@Override
public void onResponse(Call> call, Response> response) {
Log.d("ApiService", "Successfully response fetched");
loading.dismiss();
if (response.code() == 200 && response.isSuccessful()) {
clases = response.body();
showList();
} else {
Log.d("APIPlug", "No item found");
}
}
});
}
//Our method to show list
private void showList() {
Log.d("ApiService", "Show List");
ClaseAdapter adapter = new ClaseAdapter(getActivity(), clases);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
Clase clickedObj = (Clase) parent.getItemAtPosition(position);
//If you have detail activity about your object define in here
/*
Intent detail = new Intent(getContext(), ActorDetail.class);
detail.putExtra("actorObject", clickedObj);
startActivity(detail);
*/
}
});
}
}
这是我希望显示在ListView中的Web服务的实际响应:
[
[1, "halterofilia", "Profesor: XXXX YYYY. Clase de Halterofilia."],
[2, "crossfit", "Profesor:ZZZZ TTTT. Clase de CrossFit"],
[3, "halterofilia", "Profesor:XXXX YYYY. Clase de halterofilia"]
]
编辑:
来自Android Studio的回溯:
D/OkHttp: --> GET http://10.0.2.2:5000/gym/api/v1.0/clases http/1.1
D/OkHttp: --> END GET
D/OkHttp: <-- 200 OK http://10.0.2.2:5000/gym/api/v1.0/clases (8ms)
D/OkHttp: Content-Type: text/html; charset=utf-8
D/OkHttp: Content-Length: 192
D/OkHttp: Server: Werkzeug/0.11.11 Python/2.7.12
D/OkHttp: Date: Sat, 08 Oct 2016 23:19:00 GMT
D/OkHttp: OkHttp-Sent-Millis: 1475968739892
D/OkHttp: OkHttp-Received-Millis: 1475968739897
D/OkHttp: [[1, "halterofilia", "Profesor: XXXX YYYY. Clase de Halterofilia."], [2, "crossfit", "Profesor:ZZZZ TTTT. Clase de CrossFit"], [3, "halterofilia", "Profesor:XXXX YYYY. Clase de halterofilia"]]
D/OkHttp: <-- END HTTP (192-byte body)
D/ApiService: Error Occured: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 3 path $[0]
这是来自我正在运行Web服务的PyCharm:
* Detected change in 'D:\\Proyectos\\PyCharm\\api-prueba\\api-prueba.py', reloading
* Restarting with stat
* Debugger is active!
* Debugger pin code: 195-275-846
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [09/Oct/2016 00:21:38] "GET /gym/api/v1.0/clases HTTP/1.1" 200 -
1 回答
从这个错误中,您可以说它与改造期望的内容以及您的API为您提供的内容不匹配 . 更具体地说,看起来改装期望单个对象,但是您的API返回一个数组 . 我猜你正在尝试获取Clases对象的列表,而你的API发送一个...数组的数组 .
为了实现这一点,我会尝试获得这样的API响应:
(id,名称和 Headers 将替换为db字段的实际名称)
这是一个链接,应该指导您如何实现python代码的更改:Python converting mysql query result to json