0

In my C# app I access a NetApp REST API. I am using RestSharp to access the API. No problem with Get requests. However when I try a Put request I alwas get: "Invalid JSON input. Unexpected character in stream: r around 0:0."

Added: I'm stuck with RestSharp version 105.2.3 because of other dependencies. Its a huge application and moving to a newer version of RestSharp would need changes in several modules as well as moving to another .Net version. AddStringBody method would maybe help, but is not present in the version I am using.

The method called, prepares and sends the request, as well as checks the result.

    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;
    }

The RestClientCustom is only to allow debugging, when working against the productive system.

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();
        }
    }
}

The Post JSON is created with the following class.

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; }
    }
}

I use a Newtonsoft.Json serializer in order to get the correct naming in the body JSON, as well als filter null values. An example looks as follows ...

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

I can take this JSON string and post it via curl, powershell or test it on the target system directly and it works. However when I use the RestSharp implementation, I get above error.

I tried the normal serializer. But it does not generate the correct JSON (capitals). I added the JSON string directly as parameter in the code, no luck. Now I use the Newtonsoft.Json serializer. But I do not think the serialization is the problem. In the debugger I see the parameters and the URI passed in the request object. They seem ok.

I suppose I am missing something. A hint would be appreciated.

If found the solution ... In the method CreateQtree I used to add to query parameters.

        request.AddParameter("return_records", "false");
        request.AddParameter("return_timeout", 15);

When I add them more sprecific ...

        request.AddParameter("return_records", "false", ParameterType.QueryString);
        request.AddParameter("return_timeout", 15, ParameterType.QueryString);

It works. It seems the two parameters are not added correctly if I do not specify them as query parameters.

  • This might be helpful: https://restsharp.dev/usage.html#request-body – madreflection Jun 16 '22 at 19:02
  • My crystal ball says that the way you're adding the request body makes it think the already-serialized payload (string containing JSON) is something that still needs to be serialized, but I can't conceive of what it could want to do to produce an 'r' character at the start of the stream. Following one of the recommendations in the usage documentation may fix the problem, anyway. – madreflection Jun 16 '22 at 19:05
  • As stated, I am using RestShart version 105.2.3. There the serializer does not handle the capitals correctly when serializing. I tried adding a custom one, but this did not help. Same error. There is (unfortunately) no AddStringBody in my version. – Philipp Hungerbühler Jun 16 '22 at 19:10
  • I urge you add that to the post (question) body. Simply stating the upper version limit requires the reader to be intimately familiar with the version history to be aware that the method is not available. It's also important to indicate that you've already seen that it might help, if only it were available in the version you have to use. You're focusing on the serializer without indicating *why* that's necessary, so you're bound to get suggestions that you already know won't work. Save everyone the hassle. – madreflection Jun 16 '22 at 19:15
  • I'm not sure if serialization is the problem. the "r" could come from \r but I do not understand how this could be introduced, the way I put together the request. I added the version limit to the question. – Philipp Hungerbühler Jun 16 '22 at 20:18
  • If the input stream contains a carriage return, it probably would be ignored while scanning for the start of a token. If it contains the `'\\'` and `'r'` characters at the start, it would complain about the backslash in the message, not the `'r'`. The chances of either happening are extremely remote. – madreflection Jun 16 '22 at 20:20
  • `AddParameter` for POST requests with the body always adds the parameter as the form parameter. It only adds them as a query parameter for GET requests, this behaviour is the same for 105 and for the latest versions of RestSharp: https://restsharp.dev/usage.html#get-or-post – Alexey Zimarev Jul 07 '22 at 18:12

0 Answers0