登录后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
相关产品推荐
相关产品推荐

