Day 30--Retrofit 登入练习

前置作业

  • build.gradle dependencies记得加入使用retrofit2
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
  • AndroidManifest.xml : 要求网路权限
<uses-permission android:name="android.permission.INTERNET" />

interface Api

登入之前须要先有帐号,所以要先注册
提供注册的网址是https://k88d02.ml/api/register

因爲後端的注册请求方法是POST,发request须要带BODY的资料
格式爲formdata如下

建立interface包含一个register funtion
还有使用@FormUrlEncoded搭配各栏位@Field的annotation

private const val BASE_URL = "https://k88d02.ml/api/"

interface ApiService {
    @Headers("Content-type: application/json","Accept: application/json")
    @POST("register")
    @FormUrlEncoded
    fun register(
        @Field("name") name: String,
        @Field("password") password: String,
        @Field("email") email: String
    ): Call<RegisterResponse>
}

object Api {

    private val retrofit = Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl(BASE_URL)
        .build()

    val retrofitService: ApiService = retrofit.create(ApiService::class.java)
}

data class

服务器送回的response是JSON

所以建立一个data class RegisterResponse

data class RegisterResponse(
    @SerializedName("success")
    var isSuccess: Boolean = true,
    @SerializedName("message")
    var message: String = "",
    @SerializedName("data")
    var data: Nothing? = null
)

POSTMAN

建议先测试连线是否正常,使用postman这个软件
Send发送

可获得response,表示连线正常

{
"success": false,
"message": "email已使用",
"data": null
}

注册

接着就做一个登入页面,在程序呼叫api

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        btn_register.setOnClickListener {
            hideKeyboard(textView, textView)
            Api.retrofitService
                .register(
                    ed_account.text.toString(),
                    ed_password.text.toString(),
                    ed_email.text.toString()
                )
                .enqueue(object : retrofit2.Callback<RegisterResponse> {
                    override fun onFailure(call: Call<RegisterResponse>, t: Throwable) {
                        Log.e("Failed", t.toString())
                    }

                    override fun onResponse(call: Call<RegisterResponse>, response: Response<RegisterResponse>) {

                        if (response.isSuccessful) {    //request 200,注册成功
                            Toast.makeText(requireActivity(), response.body()?.message, Toast.LENGTH_SHORT).show()
                            Log.d("Success!", response.body().toString())
                        }else{                          //request 400,注册失败
                            Toast.makeText(requireActivity(), response.body()?.message, Toast.LENGTH_SHORT).show()
                            Log.d("Success!", response.body().toString())
                            Toast.makeText(requireActivity(), "注册失败", Toast.LENGTH_SHORT).show()
                        }
                    }

                })
        }

    }

密码栏位可以加一个属性,让输入隐藏

   android:inputType="textPassword"


可以看到第一次显示注册失败,因爲密码只输入了4个
而服务器要求6-12个,改爲8个後就显示注册成功了

logcat

D/Success!: RegisterResponse(isSuccess=true, message=register success , please login, data=null)

登入

这边有点搞错,原来是用email加password登入
所以我的版面栏位稍微改一下
一样是POST,要带的资料是formdata

所以interface加一个login funtion

interface ApiService {
    //    @Headers("Content-type: application/json","Accept: application/json")
    @POST("register")
    @FormUrlEncoded
    fun register(
        @Field("name") name: String,
        @Field("password") password: String,
        @Field("email") email: String
    ): Call<RegisterResponse>

    @POST("login")
    @FormUrlEncoded
    fun login(
        @Field("email") name: String,
        @Field("password") password: String
    ): Call<LoginResponse>
}

服务器login获得的response

再建立一个data class LoginResponse
里面的rememberToken是每次登入成功时,会取得的一组token

data class LoginResponse(
    @SerializedName("data")
    val `data`: Data = Data(),
    @SerializedName("message")
    val message: String = "",
    @SerializedName("success")
    val success: Boolean = false // true
) {
    data class Data(
        @SerializedName("remember_token")
        val rememberToken: String = "", // TeR5GEi4ftp4BtKqM65Q7LYB1R0dbeY5n22ZVCulgokPigme2UFOH12VEVol
        @SerializedName("token_expire_time")
        val tokenExpireTime: String = "", // 2020/09/19 23:10:13
        @SerializedName("user_id")
        val userId: Int = 0 // 3
    )
}

先注册一组帐号[email protected] / 密码asdfasdf
并确认注册成功

D/Success!: RegisterResponse(isSuccess=true, message=register success , please login, data=null)

再按下登入,可以看到显示login的Toast有取得token

Toast.makeText(requireActivity(), response.body()?.data?.rememberToken, Toast.LENGTH_SHORT).show()

多按几次登入,每次拿到的token都会不一样

D/Success!: RegisterResponse(isSuccess=true, message=register success , please login, data=null)
D/Success!: LoginResponse(data=Data(rememberToken=Zs5H2xrUh7ZYIW95urGJvY2G4rozZbvtfwIqNdZ6oz1TlmLM93pyILKZUVSt, tokenExpireTime=2020/10/05 15:40:45, userId=36), message=, success=true)
D/Success!: LoginResponse(data=Data(rememberToken=yec7MPpzKUELbBOwLYge7CmDSXnrKlXUdmQGI0VEL2z4gpg7mnhMqFZzWb6t, tokenExpireTime=2020/10/05 15:40:52, userId=36), message=, success=true)
D/Success!: LoginResponse(data=Data(rememberToken=gRA8pwe33OStUjG0XbQEFQvzTtZpECW0mAh1oXmhcgOcSpo2Xuz7tojSsN4W, tokenExpireTime=2020/10/05 15:41:28, userId=36), message=, success=true)

感谢工作室与铁人赛,虽然30天还只是一个起点
但让我有养成学习後要记录的习惯,感觉能够更帮助吸收
期望能持续下去!


<<:  [今晚我想来点 Express 佐 MVC 分层架构] DAY 30 - 是结束,也是开始

>>:  DAY30-结语

Day 5 Capsule的应用(上)

前言 由於前几天讲了capsule network,attention的笔记我还在制作,因此先来讲讲...

#21-用Canvas做科技感的动态球!(+什麽时候该用CSS/SVG/Canvas?)

今天正式进入Canvas的世界了! 老样子先看成品: 今天来做点科技感的画面,橘色是滑鼠的游标,这个...

30天轻松学会unity自制游戏-关卡场景制作

如果按照之前的教学到这一步,最基本的游戏架构已经产生了,大部分游戏都是先制作出基本架构,之後再延伸出...

什麽是战略分析 (Strategic Analysis)?如何使用工具进行此操作

您是否希望提高您的竞争地位? 应对外部威胁?识别新的机会或风险? 扩大您在新市场的影响力? 战略分...

Day9-TypeScript(TS)的介面型别(Interface)Part 2

今天要来讲介面型别的使用范例。 通常我们会使用介面来定义函式型别,程序码如下, interface ...