You need to enable JavaScript to run this app. Flutter调用NSE Nifty期权链API偶发FormatException该如何解决?
优惠活动
大模型
产品
解决方案
定价
更多

Flutter调用NSE Nifty期权链API偶发FormatException该如何解决?

问题根因

你遇到的格式化报错本质是服务端返回了HTML格式的拦截页面而非预期JSON,而现有代码没有做前置校验就直接解析内容,具体问题点有三个:

  • NSE的公开接口有严格的反爬校验机制:会校验请求携带的浏览器标识、来源页信息、会话Cookie,不满足要求的请求会被直接返回HTML格式的拦截跳转页,也就是你报错栈里看到的<!DOCTYPE html>开头的内容。
  • 现有代码对返回内容没有任何校验:不管接口返回什么状态、什么格式的内容,都直接把响应体丢给jsonDecode,一旦拿到HTML内容就必然触发格式化异常。
  • 重启应用偶发恢复的原因是:NSE的临时拦截策略基于客户端IP、临时会话标记随机触发,裸请求的无状态特性导致拦截结果不稳定,但只要请求头和会话不合规,随机触发拦截是必然结果,不是偶发的代码bug。
修复方案

1. 补全接口要求的合规请求逻辑

NSE接口要求请求必须携带和浏览器访问一致的基础请求头,同时需要维护可复用的会话客户端来自动保存服务端下发的Cookie,否则大概率被拦截。修改后的请求代码如下:

import 'dart:convert';
import 'package:http/http.dart';

final optionChainUrl = 'https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY';
// 全局复用HTTP客户端,自动维护会话Cookie
final sharedHttpClient = Client();

Future<Map<String, dynamic>> fetchNiftyData() async {
  final response = await sharedHttpClient.get(
    Uri.parse(optionChainUrl),
    headers: {
      'Accept': 'application/json, text/plain, */*',
      'Accept-Language': 'en-US,en;q=0.9',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
      'Referer': 'https://www.nseindia.com/option-chain',
    },
  );

  // 前置校验响应状态
  if (response.statusCode != 200) {
    throw Exception('请求失败,状态码:${response.statusCode}');
  }
  // 校验返回内容类型,避免把HTML当成JSON解析
  if (!response.headers['content-type']?.contains('application/json') ?? true) {
    throw Exception('接口返回非JSON内容,可能被反爬拦截');
  }

  return jsonDecode(response.body) as Map<String, dynamic>;
}
  • 注意:不要每次请求都新建Client实例,否则会话Cookie无法保存,每次请求都是无状态的裸连接,依然会被随机拦截。

2. 修复页面层的异常捕获逻辑

现有页面代码直接对异步请求结果做赋值,没有加任何异常捕获,一旦请求抛错就会触发未处理的UI层异常。修改后的页面逻辑需要补充加载态、错误态的展示,同时捕获所有请求异常:

class _NiftyScreenState extends State<NiftyScreen> {
  Map<String, dynamic>? niftyDetails;
  String? errorText;
  bool isLoading = true;

  @override
  void initState() {
    super.initState();
    loadNiftyData();
  }

  Future<void> loadNiftyData() async {
    try {
      final data = await fetchNiftyData();
      setState(() {
        niftyDetails = data;
        isLoading = false;
      });
    } catch (e) {
      setState(() {
        errorText = e.toString();
        isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: isLoading
          ? const LinearProgressIndicator()
          : errorText != null
              ? Center(child: Text('加载失败:$errorText'))
              : ListView.builder(
                  itemCount: niftyDetails!["filtered"]["data"].length,
                  itemBuilder: (_, index) {
                    return ListTile(
                      title: Text(
                          '${niftyDetails!["filtered"]["data"][index]["strikePrice"]}'),
                    );
                  },
                ),
    );
  }
}
API调用通用最佳实践
  • 拿到响应后永远不要直接做JSON解析:先校验响应状态码、返回内容类型,确认是预期格式后再解析,避免模糊的格式化报错干扰问题定位。
  • 所有异步网络请求必须加try-catch捕获全部异常,不要把未处理的异常抛到UI层导致页面崩溃。
  • 对反爬严格的公开接口,复用HTTP客户端实例来维护会话状态,减少被识别为爬虫的概率。
  • 对临时拦截的场景,加有限度的重试逻辑(间隔1-2秒重试1-2次即可),不要无限重试触发更严格的访问限制。

内容的提问来源于stack exchange,提问作者Manish Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 11:15:04