You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

登录后Volley请求DjangoRestApi遇401错误,切换Fragment后恢复的解决方法

解决Volley+Django JWT请求首次401的问题

从你的描述来看,核心问题应该是首次进入第二个Fragment时,Token还没被正确读取或设置到请求头中,而切换回来后Fragment重新初始化,此时Token已经就绪,所以请求正常。下面是具体的排查和解决方法:

1. 确保登录成功后再跳转,且Token同步写入SharedPreference

首先要保证,只有在登录请求成功、Token已经写入SharedPreference之后,才跳转到第二个Fragment。Volley的onResponse回调是在主线程执行的,所以这里写入SharedPreference是安全的:

// 登录请求的onResponse回调
@Override
public void onResponse(JSONObject response) {
    try {
        String jwtToken = response.getString("token");
        // 同步写入SharedPreference
        SharedPreferences prefs = getActivity().getSharedPreferences("AuthPrefs", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString("jwt_token", jwtToken);
        editor.commit(); // 用commit()确保同步写入,apply()是异步但这里不影响,不过commit更稳妥
        
        // 写入完成后再跳转
        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.action_login_to_fragment2);
    } catch (JSONException e) {
        e.printStackTrace();
    }
}

2. 每次发起请求都重新读取Token,不要缓存成员变量

很多时候问题出在Fragment初始化时,把Token存在成员变量里,但此时Token还没写入,导致成员变量为null。每次发起请求都直接从SharedPreference读取最新的Token:

private void fetchProtectedData() {
    SharedPreferences prefs = getActivity().getSharedPreferences("AuthPrefs", Context.MODE_PRIVATE);
    String jwtToken = prefs.getString("jwt_token", null);
    
    if (jwtToken == null) {
        // Token不存在,跳回登录页
        NavHostFragment.findNavController(this).navigate(R.id.action_fragment2_to_login);
        return;
    }
    
    StringRequest request = new StringRequest(Request.Method.GET, "https://your-django-api/protected-endpoint",
            response -> {
                // 处理成功响应
                Log.d("Fragment2", "Data received: " + response);
            },
            error -> {
                // 处理错误,比如401时跳回登录
                if (error.networkResponse != null && error.networkResponse.statusCode == 401) {
                    NavHostFragment.findNavController(this).navigate(R.id.action_fragment2_to_login);
                }
            }) {
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            Map<String, String> headers = new HashMap<>();
            // 注意格式:Bearer + 空格 + Token,Django JWT默认要求这个格式
            headers.put("Authorization", "Bearer " + jwtToken);
            return headers;
        }
    };
    
    // 禁用请求缓存,避免401响应被缓存
    request.setShouldCache(false);
    Volley.newRequestQueue(getActivity()).add(request);
}

3. 调整Fragment发起请求的时机

不要在onCreateView里直接发起请求,而是在onResume或者通过ViewModel的状态通知来触发请求:

方式一:用ViewModel监听Token状态

创建一个共享的AuthViewModel,登录成功后更新Token,第二个Fragment观察Token变化再发起请求:

// AuthViewModel.java
public class AuthViewModel extends ViewModel {
    private final MutableLiveData<String> currentToken = new MutableLiveData<>();

    public void setToken(String token) {
        currentToken.setValue(token);
    }

    public LiveData<String> getCurrentToken() {
        return currentToken;
    }
}

在登录Fragment的onResponse里设置ViewModel的Token:

AuthViewModel authViewModel = new ViewModelProvider(requireActivity()).get(AuthViewModel.class);
authViewModel.setToken(jwtToken);

在第二个Fragment的onViewCreated里观察Token:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    
    AuthViewModel authViewModel = new ViewModelProvider(requireActivity()).get(AuthViewModel.class);
    authViewModel.getCurrentToken().observe(getViewLifecycleOwner(), token -> {
        if (token != null) {
            fetchProtectedData();
        }
    });
    
    // 从SharedPreference恢复Token,防止ViewModel数据丢失(比如屏幕旋转)
    SharedPreferences prefs = getActivity().getSharedPreferences("AuthPrefs", Context.MODE_PRIVATE);
    String savedToken = prefs.getString("jwt_token", null);
    if (savedToken != null) {
        authViewModel.setToken(savedToken);
    }
}

方式二:在onResume里检查Token并发起请求

@Override
public void onResume() {
    super.onResume();
    SharedPreferences prefs = getActivity().getSharedPreferences("AuthPrefs", Context.MODE_PRIVATE);
    String jwtToken = prefs.getString("jwt_token", null);
    if (jwtToken != null) {
        fetchProtectedData();
    } else {
        // 跳回登录
        NavHostFragment.findNavController(this).navigate(R.id.action_fragment2_to_login);
    }
}

4. 排查Django API的JWT配置

确保你的Django Rest Framework JWT配置正确:

  • 检查settings.py里的JWT_AUTH设置,比如JWT_AUTH_HEADER_PREFIX是否是Bearer(默认是),如果你的配置改成了其他值,请求头里也要对应修改。
  • 确保Token没有过期,可以用JWT解码器验证一下Token的有效期。

5. 禁用Volley的请求缓存

有时候第一次的401响应会被Volley缓存,导致后续请求还是用旧的响应。所以在发起请求时禁用缓存:

request.setShouldCache(false);

按照上面的步骤排查,应该能解决你遇到的首次请求401、切换回来正常的问题。

内容的提问来源于stack exchange,提问作者Satyam Gondhale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:38:34