如何处理旧移动应用程序版本的后端更改

每当我们拥有一项新功能时,我们都想添加到现有应用程序中,作为Web开发人员,我们都将同时更新前端和后端。然后,我们将更改部署到生产环境中,并将其称为一天。

但是,在移动应用程序的情况下,同一概念并不总是适用。因为并非每个人都会自动更新该应用程序。因此,在移动应用程序的较旧版本中,对后端代码的新更改可能无法正常工作。这是新版本后的大多数用户。

假设我们有一个应用程序,可以让用户跟踪他们的每月费用。我们有一个React Native应用程序,可以从用户那里获取输入和将费用存储在数据库中的后端。以下是我们用来与后端通信的JSON格式:

1{
2    data: [
3        {
4            "id": 1,
5            "amount": 100,
6            "date": "2021-01-01",
7            "category": "Food",
8            "subcategory": "Groceries",
9            "description": "Bought groceries for the month",
10        },
11        {
12            "id": 2,
13            "amount": 200,
14            "date": "2021-01-02",
15            "category": "Transportation",
16            "subcategory": "Gas",
17            "description": "Filled up the gas tank",
18        }
19    ]
20}

假设我们有一个正在消耗API的React Native应用程序,则我们正在显示列表中的费用。像这样:

1<View>
2    {result?.data?.map((expense) => (
3        <View>
4            <Text>{expense.amount}</Text>
5            <Text>{expense.date}</Text>
6            <Text>{expense.category}</Text>
7            <Text>{expense.subcategory}</Text>
8            <Text>{expense.description}</Text>
9        </View>
10    ))}
11</View>

但是,如果我们现在想添加一个使用户还可以存储其收入的功能怎么办?为了实现这一目标,我们需要更新后端代码,以便将收入存储在数据库中。在检索数据时,我们需要以以下JSON格式将数据寄回:

1{
2    data: {
3        expenses: [
4            {
5                "id": 1,
6                "amount": 100,
7                "date": "2021-01-01",
8                "category": "Food",
9                "subcategory": "Groceries",
10                "description": "Bought groceries for the month",
11            },
12            {
13                "id": 2,
14                "amount": 200,
15                "date": "2021-01-02",
16                "category": "Transportation",
17                "subcategory": "Gas",
18                "description": "Filled up the gas tank",
19            }
20        ],
21        incomes: [
22            {
23                "id": 1,
24                "amount": 10000,
25                "date": "2021-01-01",
26                "category": "Salary",
27                "subcategory": "Full-time",
28                "description": "Received salary for the month",
29            },
30            {
31                "id": 2,
32                "amount": 2050,
33                "date": "2021-01-02",
34                "category": "Freelance",
35                "subcategory": "Writing",
36                "description": "Received freelance payment for the month",
37            }
38        ]
39    }
40}

因此,移动应用程序将必须更新代码以处理新的JSON格式。

1<View>
2-    {result?.data?.map((expense) => (
3+    {result?.data?.expenses?.map((expense) => (
4        <View>
5            <Text>{expense.amount}</Text>
6            <Text>{expense.date}</Text>
7            <Text>{expense.category}</Text>
8            <Text>{expense.subcategory}</Text>
9            <Text>{expense.description}</Text>
10        </View>
11    ))}
12</View>

但是在移动应用程序的较旧版本中,代码仍将使用旧的JSON格式。因此该应用将崩溃。将数据保存到后端时也适用。移动应用程序的较旧版本将无法以新的JSON格式保存数据,以防我们有一个验证器,该验证器在将JSON格式保存到数据库之前检查了JSON格式。但是在这里,我们只会谈论响应格式。我们确定,一旦您知道技巧,您就可以自行处理“有条件保存到数据库”部分。

为了解决此问题,我们可以有条件地为移动应用程序的旧版本返回旧的JSON格式。并返回新版本的移动应用程序的新JSON格式。但是,后端服务器将如何知道用户使用哪个版本的移动应用程序?那就是库称为“ React-Native-Device-Info”的库。使用此库可以使用移动应用程序的版本,并使用自定义标题“ MobileAppersion”将其发送到后端服务器。在后端,我们可以检查标题“ mobileAppversion”是否存在,并且大于或等于我们要支持的移动应用程序的版本。如果是这样,我们可以返回新的JSON格式,否则我们可以返回旧的JSON格式。

如果您使用的是Apisauce(建议使用),则可以设置这样的标题:

1import DeviceInfo from "react-native-device-info"
2
3...
4setAllHeaders(value?: string, appVersion?: string) {
5    api.apisauce.setHeader("Authorization", `Bearer ${value}`)
6    api.apisauce.setHeader("MobileAppVersion", appVersion || "")
7}
8...
9
10setAllHeaders(token, DeviceInfo.getVersion())

或者,如果您不使用Apisauce,则可以手动设置标题:

1async function getData(url: string, appVersion: string) {
2    const response = await fetch(url, {
3        headers: {
4            'Authorization': 'Bearer Token',
5            'MobileAppVersion': appVersion || ""
6        }
7    })
8    const data = await response.json()
9    return data
10}
11
12...
13// get the data from the api (e.g. inside useEffect, or a button press)
14const result = await getData("https://api.example.com", DeviceInfo.getVersion())
15...

然后,在后端代码中,您可以根据应用程序版本有条件地返回响应。

1const express = require('express')
2const app = express()
3
4app.get('/', (req, res) => {
5    const appVersion = req.headers['MobileAppVersion']
6    if (appVersion && appVersion >= '2.0.0') { // if the app version is 2.0.0 or higher
7        res.json({ data: { expenses: [...], incomes: [...]} })
8    }
9    else {
10        // if the app version is lower than 2.0.0, older response format
11        res.json({ data: [] })
12    }
13})

或者,如果您与Django一起使用Python,则可以做类似的事情:

1from django.http import JsonResponse
2
3class get_data(request):
4    def get(self, request):
5        app_version = request.headers.get('MobileAppVersion', '1.0.0')
6        if app_version and app_version >= '2.0.0':
7            # if the app version is 2.0.0 or higher
8            return JsonResponse({'data': {expenses: [...], incomes: [...]}})
9        else:
10            # if the app version is lower than 2.0.0, older response format
11            return JsonResponse({'data': [...]})