Ошибка чтения JObject из JsonReader. Путь '', строка 0, позиция 0»

Я использую следующий код для вызова почтового интерфейса в Visual Studio 2022:

private List<Token> LoginToken(string username, string password)
{
 string api = "";
 List<Token> list = new List<Token>();
 string url = "";
 Dictionary<string, string> dics = new Dictionary<string, string>
 {
   { "username", username },
   { "password", password },
   { "loginType", "account" },
   { "grantType", "password" }
 };
 Task<string> task = Api.InvokeWebapi(url, api, "POST", dics);
 string result = task.Result;

 if (result != null)
 {
     JObject jsonObj = null;
     jsonObj = JObject.Parse(result);
     DataInfo info = new DataInfo();
     info.statusCode = Convert.ToInt32(jsonObj["code"]);
     info.message = jsonObj["message"].ToString();
     if (info.statusCode == 1)
     {
         JArray jlist = JArray.Parse(jsonObj["data"].ToString());
         for (int i = 0; i < jlist.Count; ++i)
         {
             Token ver = new Token();
             JObject tempo = JObject.Parse(jlist[i].ToString());
             ver.access_token = tempo["access_token"].ToString();
             ver.token_type = tempo["token_type"].ToString();
             ver.expires_in = Convert.ToInt32(tempo["expires_in"]);
             ver.scope = tempo["scope"].ToString();
             ver.name = tempo["name"].ToString();
             ver.my_Corp = tempo["my_Corp-Code"].ToString();
             ver.userId = Convert.ToInt32(tempo["userId"]);
             ver.username = tempo["username"].ToString();
             ver.jti = tempo["jti"].ToString();
             list.Add(ver);
         }
     }
 }
 return list;
}


public async Task<string> InvokeWebapi(string url, string api, string type, Dictionary<string, string> dics)
{
    string result = string.Empty;
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", "Basic 1111");
    client.BaseAddress = new Uri(url);

    client.Timeout = TimeSpan.FromSeconds(510);

    if (type.ToLower() == "put")
    {
        HttpResponseMessage response;
        if (dics.Keys.Contains("input"))
        {
            if (dics != null)
            {
                foreach (var item in dics.Keys)
                {
                    api = api.Replace(item, dics[item]).Replace("{", "").Replace("}", "");
                }
            }
            var contents = new StringContent(dics["input"], Encoding.UTF8, "application/json");
            response = client.PutAsync(api, contents).Result;
            if (response.IsSuccessStatusCode)
            {
                result = await response.Content.ReadAsStringAsync();
                return result;
            }
            return result;
        }

        var content = new FormUrlEncodedContent(dics);
        response = client.PutAsync(api, content).Result;
        if (response.IsSuccessStatusCode)
        {
            result = await response.Content.ReadAsStringAsync();
            return result;
        }
    }
    else if (type.ToLower() == "post")
    {
        var content = new FormUrlEncodedContent(dics);

        HttpResponseMessage response = client.PostAsync(api, content).Result;
        if (response.IsSuccessStatusCode)
        {
            result = await response.Content.ReadAsStringAsync();
            return result;
        }
    }
    else if (type.ToLower() == "get")
    {
        HttpResponseMessage response = client.GetAsync(api).Result;

        if (response.IsSuccessStatusCode)
        {
            result = await response.Content.ReadAsStringAsync();
            return result;
        }
    }
    else
    {
        return result;
    }
    return result;
}
}

Я использую приведенный выше код для вызова почтового интерфейса в Visual Studio 2022, но в jsonObj = JObject.Parse(result); сообщается об ошибке Newtonsoft.Json.JsonReaderException: "Error reading JObject from JsonReader. Path '', line 0, position 0.".

Обновлять:

Я зафиксировал ответ API, когда было сообщено об ошибке:

{StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Pragma: no-cache
X-Frame-Options: DENY
Referrer-Policy: no-referrer
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Date: Wed, 17 Jul 2024 08:47:38 GMT
Server: nginx/1.24.0
Content-Type: application/json
Expires: 0
}}

Можете ли вы поделиться полученным ответом API? Похоже, что ответ API не является объектом.

Yong Shun 17.07.2024 10:44

@YongShun Привет, { "code": "00000", "message": "success", "data": { "access_token": "oLWzICg", "token_type": "bearer", "expires_in": 17017, "scope ": "читать и писать", "имя": "Дайнел", "my_Corp-Code": "C1723", "userId": 43452, "имя пользователя": "00001744", "jti": "80f2837c" } }

Wenbin Geng 17.07.2024 10:46

Вы уверены, что это действительно возвращается? Или это то, чего вы ожидаете? Вы действительно прошли через свой код?

Jamiec 17.07.2024 10:50

Он работает нормально. Демо. Вы подтверждаете, что result является допустимой строкой JSON?

Yong Shun 17.07.2024 10:52
Ошибка 415 означает, что вы отправляете полезную нагрузку запроса, которая не поддерживается в этом API. Возможно, вам потребуется предоставить код, показывающий, как вы отправляете полезную нагрузку (тело запроса JSON, форму и т. д.) и какой тип полученной полезной нагрузки ожидает API.
Yong Shun 17.07.2024 10:54

@Nodedefender, так что текста JSON вообще нет, только ответ об ошибке 415 без тела. То, что вы опубликовали, — это HttpResponseMessage, преобразованный в JSON, а не фактический ответ. Проблема внутри Api.InvokeWebapi. Делает запрос с неправильным содержанием и не проверяет ответ на ошибки.

Panagiotis Kanavos 17.07.2024 10:54

В любом случае вам не понадобится весь этот код для анализа ответа JSON. Если Token соответствует ответу JSON, достаточно var token=await response.Content.ReadAsJsonAsync<Token>();. Даже если вы настаиваете на использовании временных строк (почему???), вы можете использовать var token=JsonSerializer.Deserialize<Token>(json)

Panagiotis Kanavos 17.07.2024 10:58

Вы можете удалить вторую половину кода, если воспользуетесь var response=await client.PostAsJsonAsync(url,actualObject);. Этот метод создает JsonContent из фактического объекта, который сериализует его непосредственно в поток запросов, когда запрос будет выполнен, используя правильные заголовки. Нет необходимости в словарях или ручной настройке типа контента.

Panagiotis Kanavos 17.07.2024 11:00

Метод InvokeWebapi использует FormUrlEncodedContent для отправки того, что, как я предполагаю, является содержимым JSON. Вот почему сервер отклоняет запрос. Весь метод полон проблем и ничего не упрощает. Он смешивает синхронные и асинхронные вызовы, создает новый HttpClient вместо повторного использования одного, и даже когда application/json используется в качестве типа контента, фактический контент вообще не является JSON. Ошибки скрываются, а не обрабатываются, поэтому невозможно узнать, работает ли что-нибудь.

Panagiotis Kanavos 17.07.2024 11:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Метод InvokeWebApi полон ошибок, отправляет содержимое FORM в сервис, который явно ожидает JSON, и скрывает ошибки, возвращая пустую строку, если что-то пойдет не так. Удаленная служба отклоняет вызов из-за плохого содержимого, но вызывающий код все равно пытается десериализовать пустую строку, что приводит к ошибке.

Реальное решение — полностью заменить InvokeWebApi соответствующими вызовами. Прежде всего, HttpClient является потокобезопасным и предназначен для повторного использования. Создание нового каждый раз является серьезной ошибкой, приводящей к исчерпанию сокетов.

Экземпляр HttpClient должен быть полем или параметром метода. В ASP.NET Core самый простой способ — внедрить его с помощью DI. Все, что для этого требуется, — это вызов builder.Services.AddHttpClient() и добавление HttpClient в качестве параметра конструктора к контроллеру или сервису, которому он нужен.

builder.Services.AddHttpClient(client=>{
    client.DefaultRequestHeaders.Add("Authorization", "Basic 1111");
    client.BaseAddress = new Uri(siteUrl);
});

Независимо от того, откуда взялся HttpClient, весь метод LoginToken можно упростить до следующего:

async Task<List<Token>> LoginToken(string username, string password)
{
    var request = new {
       username=username,
       password=password ,
       loginType = "account",
       grantType = "password"
    };

    var resp=await _client.PostAsJsonAsync(relativeUrl,request);
    if (!resp.IsSuccessStatusCode)
    {
        //Actually handle the problem, the application can't continue without a token
        _logger.LogError("Token call failed with {code}:{reason}",resp.StatusCode,resp.ReasonPhrase);
        throw new Exception(.....);
    }
    DataInfo info=await resp.Content.ReadAsJsonAsync<DataInfo>();
    if (info.Code == "0001") //Or whatever the success code is
    {
        return info.Data;
    }
    else 
    {
        _logger.LogError("Token retrieval failed with {code}:{message}",info.statusCode,info.message);
        //Actually do something about the error. 
        //The application can't proceed without a login token
    }
}

Метод PostAsJsonAsync сериализует объект запроса непосредственно в поток запросов как JSON и проверяет, используется ли правильный тип контента.

Код предполагает, что объекты DataInfo и Token соответствуют JSON ответа, как и должно быть. Например :

public class DataInfo
{
    public int Code {get;set;}
    public string Message {get;set;}
    List<Token> Data{get;set;}
}

или

public class DataInfo<T>
{
    public int Code {get;set;}
    public string Message {get;set;}
    List<T> Data {get;set;}
}

Атрибут [JsonPropertyName("code")] можно использовать для сопоставления code со свойством с именем StatusCode.

public class DataInfo<T>
{
    [JsonPropertyName("code")]
    public int StatusCode {get;set;}
    public string Message {get;set;}
    List<T> Data {get;set;}
}
Ответ принят как подходящий

Спасибо Панайотису Канавосу и всем за предложения по улучшению. Позже я внимательно изучу их. Моя проблема решена. Проблема заключалась в том, что формат SerializeObject(dics) был неправильным. Я преобразовал его позже, и он может работать успешно.

var json = JsonConvert.SerializeObject(dics);
var content = new StringContent(json, Encoding.UTF8, "application/json");
//var content = new FormUrlEncodedContent(dics);   Previous code

Другие вопросы по теме