Я использую следующий код для вызова почтового интерфейса в 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
}}
@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" } }
Вы уверены, что это действительно возвращается? Или это то, чего вы ожидаете? Вы действительно прошли через свой код?
Он работает нормально. Демо. Вы подтверждаете, что result
является допустимой строкой JSON?
@Nodedefender, так что текста JSON вообще нет, только ответ об ошибке 415 без тела. То, что вы опубликовали, — это HttpResponseMessage, преобразованный в JSON, а не фактический ответ. Проблема внутри Api.InvokeWebapi
. Делает запрос с неправильным содержанием и не проверяет ответ на ошибки.
В любом случае вам не понадобится весь этот код для анализа ответа JSON. Если Token
соответствует ответу JSON, достаточно var token=await response.Content.ReadAsJsonAsync<Token>();
. Даже если вы настаиваете на использовании временных строк (почему???), вы можете использовать var token=JsonSerializer.Deserialize<Token>(json)
Вы можете удалить вторую половину кода, если воспользуетесь var response=await client.PostAsJsonAsync(url,actualObject);
. Этот метод создает JsonContent
из фактического объекта, который сериализует его непосредственно в поток запросов, когда запрос будет выполнен, используя правильные заголовки. Нет необходимости в словарях или ручной настройке типа контента.
Метод InvokeWebapi
использует FormUrlEncodedContent
для отправки того, что, как я предполагаю, является содержимым JSON. Вот почему сервер отклоняет запрос. Весь метод полон проблем и ничего не упрощает. Он смешивает синхронные и асинхронные вызовы, создает новый HttpClient вместо повторного использования одного, и даже когда application/json
используется в качестве типа контента, фактический контент вообще не является JSON. Ошибки скрываются, а не обрабатываются, поэтому невозможно узнать, работает ли что-нибудь.
Метод 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
Можете ли вы поделиться полученным ответом API? Похоже, что ответ API не является объектом.