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

RestSharp调用NetApp REST API POST请求报Invalid JSON input错误

问题描述

在C#应用程序中使用RestSharp访问NetApp REST API时,GET请求可正常执行,但发送POST请求时始终返回错误:
"Invalid JSON input. Unexpected character in stream: r around 0:0."

环境约束:受项目其他依赖限制,当前固定使用RestSharp 105.2.3版本。由于应用体量较大,升级RestSharp需要修改多个模块并适配更高版本.NET框架,且当前版本不支持AddStringBody方法。

相关代码

请求发送核心方法

负责构造、发送请求并校验返回结果的CreateQtree方法实现:

internal bool CreateQtree(Pool pool, BaseStorageConf storageConf, RestClientCustom server) {
    bool isOk = false;
    // Prepare request query parameters.
    var request = new RestRequest("storage/qtrees");
    request.Method = Method.POST;
    request.RequestFormat = DataFormat.Json;
    request.AddParameter("return_records", "false");
    request.AddParameter("return_timeout", 15);
    // Prepare body.
    PostCreateQTree CreateQTreePost = new PostCreateQTree();
    CreateQTreePost.Name = pool.Name;
    CreateQTreePost.SecurityStyle = "unix";
    CreateQTreePost.Svm = new Svm();
    CreateQTreePost.Svm.Name = storageConf.FileServer;
    CreateQTreePost.Volume = new Volume();
    CreateQTreePost.Volume.Name = storageConf.FileSystem;

    // Assure that null values are not serialized.
    JsonSerializerSettings setting = new JsonSerializerSettings();
    setting.NullValueHandling = NullValueHandling.Ignore;
    // Using this serializer makes sure that the JsonProperty are honored.
    string body = JsonConvert.SerializeObject(CreateQTreePost, setting);
    request.AddParameter("application/json", body, ParameterType.RequestBody);

    // Execute request.
    var response = server.Execute(request);
    // Handle results.
    if (response.StatusCode == HttpStatusCode.OK) {
        // Creation of the qtree was successfull.
        TheLogger.Instance.Error(string.Format("Qtree could be created. Pool: {0}", pool.Name));
        isOk = true;
    } else {
        // There was an error, dump.
        ResponseError ErrorResponse = JsonConvert.DeserializeObject<ResponseError>(response.Content);
        TheLogger.Instance.Error(string.Format("Qtree could not be created. Pool: {0}, code: {1}, message: {2}", pool.Name, ErrorResponse.Error.Code, ErrorResponse.Error.Message));
    }
    return isOk;
}

自定义调试客户端

RestClientCustom为继承自RestClient的自定义实现,用于生产环境打印调试日志:

using Logging;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Devices.NetAppREST {

    internal class RestClientCustom:RestClient {

        private readonly bool mIsDebugMode;

        internal RestClientCustom(string baseUrl, bool isDebugMode): base(baseUrl) {
            mIsDebugMode = isDebugMode;
        }

        internal RestClientCustom(Uri baseUrl, bool isDebugMode) : base(baseUrl) {
            mIsDebugMode = isDebugMode;
        }

        public IRestResponse Execute(RestRequest request) {
            IRestResponse response;
            
            TheLogger.Instance.Debug(string.Format("REST URI:\r\n{0}", base.BuildUri(request)));
            TheLogger.Instance.Debug(string.Format("Parameters:\r\n{0}", ParameterToString(request)));
            

            if (mIsDebugMode) {
                response = new RestResponse();
                response.StatusCode = HttpStatusCode.OK;
                return response;
            } else {
                response = base.Execute(request);
            }

            TheLogger.Instance.Debug(string.Format("StatusCode:\r\n{0}", response.StatusCode));
            TheLogger.Instance.Debug(string.Format("Content:\r\n{0}", response.Content));
            TheLogger.Instance.Debug(string.Format("Headers:\r\n{0}", response.Headers));
            TheLogger.Instance.Debug(string.Format("ResponseURI:\r\n{0}", response.ResponseUri));
            TheLogger.Instance.Debug(string.Format("ErrorMessage:\r\n{0}", response.ErrorMessage));

            return response;
                
        }

        internal String ParameterToString(RestRequest request) {
            var sb = new StringBuilder();
            foreach (var param in request.Parameters) {
                sb.AppendFormat("Name: {0}, Value: {1}, Type: {2}\r\n", param.Name, param.Value, param.Type.ToString());
            }
            return sb.ToString();
        }
    }
}

请求体实体类

用于生成POST请求体JSON的实体类定义,配合Newtonsoft.Json保证字段名符合接口要求、过滤null值:

using Newtonsoft.Json;
namespace Devices.NetAppREST {
    public class PostCreateQTree {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("security_style")]
        public string SecurityStyle { get; set; }

        [JsonProperty("svm")]
        public Svm Svm { get; set; }

        [JsonProperty("volume")]
        public Volume Volume { get; set; }
    }
}

序列化后生成的示例JSON如下,经校验格式完全合法:

{"name":"G-IPH-TestPoolCreate","security_style":"unix","svm":{"name":"sy-fs-301"},"volume":{"name":"g"}}

该JSON通过curl、PowerShell或接口直接调用均可正常执行,但通过上述RestSharp实现发送始终报错。先后尝试更换RestSharp默认序列化器(会生成大写字段名不符合接口要求)、直接硬编码JSON字符串作为参数均无法解决问题,调试时查看请求参数、URI均无明显异常。

根因分析

问题出在查询参数的添加逻辑:RestSharp 105.2.3版本中,调用AddParameter方法时如果不显式指定参数类型,框架不会自动将return_records、return_timeout识别为URL查询参数,反而会将这两个参数拼接到请求体内容最前方,直接破坏原本合法的JSON结构。
报错信息中提到的意外字符r,正是被错误拼接的第一个参数return_records的首字符——服务端解析请求体时第一个字符不是JSON合法的起始符号({/[),因此抛出JSON格式非法错误。

解决方案

给两个URL查询参数显式指定参数类型为ParameterType.QueryString即可。

  • 原错误写法:
request.AddParameter("return_records", "false");
request.AddParameter("return_timeout", 15);
  • 修正后写法:
request.AddParameter("return_records", "false", ParameterType.QueryString);
request.AddParameter("return_timeout", 15, ParameterType.QueryString);

修改后POST请求即可正常调用。


内容的提问来源于stack exchange,提问作者Philipp Hungerbühler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:06:40