【问题标题】:Returning JSON via WebApi通过 WebApi 返回 JSON
【发布时间】:2019-09-20 13:31:08
【问题描述】:

我正在使用 C# 中的 HttpClient 尝试将 SSO(单点登录)集成到我们的一些自定义应用程序中。

我已经在我们的 JavaScript 应用程序中成功地做到了这一点,但我在将它集成到我们的一些 Umbraco 网站时遇到了一些困难。

到目前为止我的代码:

using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using Newtonsoft.Json;
using System.Collections.Generic;
using Umbraco.Web;
using Umbraco.Web.WebApi;

namespace Umbraco.WebApi
{
  public class TestController : UmbracoApiController
  {

    public  HttpClient client = new HttpClient();

    [HttpPost]
    public async Task<Object> GetRefreshToken(Token t)
    {
      try {
        string refToken = t.refresh_token;

        var values = new Dictionary<string, string>
        {
          { "grant_type", "refresh_token" },
          { "client_id", "CLIENTID" },
          { "client_secret", "CLIENTSECRET" },
          { "refresh_token", refToken }
        };

        var content = new FormUrlEncodedContent(values);        
        var response = await client.PostAsync("https://URL.org/Token", content);

        string responseString = await response.Content.ReadAsStringAsync();

        return responseString;

      } catch(HttpRequestException e) {
        return e;
      }
    }

    public class Token
    {
      public string refresh_token { get; set; }
    }

    public class AuthData
    {
      public string access_token { get; set; }
      public string token_type { get; set; }
      public int expires_int { get; set; }
      public string refresh_token { get; set; }
      public string userName { get; set; }
      public string client_id { get; set; }
      public DateTime issued { get; set; }
      public DateTime expires { get; set; }

    }
  }
}

哪个确实成功返回了我所追求的数据,但返回的数据存在问题(已删除敏感数据):

<z:anyType xmlns:d1p1="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" i:type="d1p1:string">
{"access_token":"XXXXXXXX","token_type":"bearer","expires_in":1199,"refresh_token":"XXXXXX","userName":"XXXXXX","as:client_id":"XXXXX",".issued":"Fri, 20 Sep 2019 13:23:48 GMT",".expires":"Fri, 20 Sep 2019 13:43:48 GMT"}
</z:anyType>

它似乎也以 XML 而不是 JSON 的形式返回?

C# 不是我最擅长的语言,所以我可能完全错了。

任何帮助表示赞赏。

【问题讨论】:

  • 当你在 Javascript 中做同样的事情时,你会得到 JSON?
  • 确实如此。在我的 javascript 应用程序中,使用完全相同的过程。数据通过 JSON 返回。然后我可以访问其属性以对用户进行身份验证。不幸的是,使用 C# 中的上述代码,JSON 似乎是一个“字符串”(我假设因为 string responseString = await response.Content.ReadAsStringAsync() 而实际响应似乎被格式化为 XML。

标签: c# json httpclient


【解决方案1】:

在您的代码中,在您获得 JSON 字符串 responseString 后,不要返回它,而是尝试以下代码。

...
string responseString = await response.Content.ReadAsStringAsync();
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(responseString, Encoding.UTF8, "application/json");
return response;

您需要将方法返回值从Task&lt;Object&gt; 更改为Task&lt;HttpResponseMessage&gt;

编辑:

要访问属性,请安装 Newtonsoft.Json 包并尝试以下代码。

var jsonString = ...
JObject jo = JObject.Parse(jsonString);
Console.WriteLine(jo["access_token"]);

【讨论】:

  • 感谢您的建议,在包含您的建议后,它现在点击“Catch”块并说:无法将类型“System.Net.Http.HttpRequestException”隐式转换为“System.Net.Http.HttpResponseMessage” '
  • 能否尝试调试代码并尝试查看异常抛出的位置?抱歉,我重新声明了变量 response 并删除了它之前的 var
  • 非常感谢,据我所知,现在正在应用标头并以 JSON 形式返回。另一个问题,如果我可以?我现在如何访问数据的属性?我已经尝试过 response.access_token 和 response["access_token"] 两者都给我一个错误?同样奇怪的是,我不得不将 Task 保持原样?否则我收到此错误:无法将类型“System.Net.Http.HttpRequestException”隐式转换为“System.Net.Http.HttpResponseMessage”
  • 您可以安装 Newtonsoft.Json 包并使用答案中添加的代码来访问属性。
  • HttpRequestException 由 HttpClient.PostAsync 抛出,并且在您的 catch 块中,不要返回异常,而是应该处理异常,例如记录它并返回类似 return new HttpResponseMessage(HttpStatusCode.InternalServerError); 或其他一些错误消息给客户端。
【解决方案2】:

解决此问题的方法之一是通过添加正确的请求标头在响应中专门请求 json 格式

Accept: application/json

这样试试

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

现在,如果服务器注意到请求标头,它将返回一个 json 给您。 它可能默认为 xml,因为您的请求中没有这样的标头,或者服务器仅支持返回 xml 响应。

编辑:如果您无法让服务器返回 json,您可以将您的 xml 字符串响应转换为 json 字符串。看看这个example。转换后,您可以从控制器正常返回您的 json 字符串。

编辑:

好的,试试下面这个示例:

var content = new FormUrlEncodedContent(new[]
                {
                new KeyValuePair<string, string>("client_id", ""),
                new KeyValuePair<string, string>("scope", ""),
                new KeyValuePair<string, string>("grant_type", "authorization_code"),
                new KeyValuePair<string, string>("redirect_uri", ""),
                new KeyValuePair<string, string>("code", ""),
                new KeyValuePair<string, string>("client_secret","")
            });

            AADTokenResponse TokenResponse = null;
            string _baseAddress = string.Format("https://yourTargetDomain.com/");

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(_baseAddress);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var responseMessage = await client.PostAsync("targetApiSegment", content);

                if (responseMessage.IsSuccessStatusCode)
                {
                    var responseString = await responseMessage.Content.ReadAsStringAsync();
                    TokenResponse = JsonConvert.DeserializeObject<AADTokenResponse>(responseString);
                }
            }

【讨论】:

  • 您好,谢谢您的回复。我试过设置这样的请求头:client.DefaultRequestHeaders.Add("Accept", "application/json");但是,当使用 Fiddler 查看请求时,似乎没有添加标头。此外,当我尝试使用 Task 时,我得到 The type or namespace name 'ActionResult' could not be found 所以我添加了 using System.Web.Mvc;然后产生一个新的错误,说 [HttpPost] 找不到...谢谢!!
  • 这是 mvc 控制器还是 web api 控制器?
  • 对不起,这是一个继承自 UmbracoApiController 的 WebApi 控制器
  • 我认为主要问题是无论出于何种原因,它都以 XML 而不是 JSON 的形式返回。而且无论我做什么,标题都不会改变,所以保持:接受:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3
  • 试试我在编辑中添加的这种方式。 client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));如果您确实设法在请求中看到标头,但仍然得到 xml,您可以处理 xml 并将其转换为 json 并将其发回,请参阅编辑
【解决方案3】:

我认为在这种情况下,FormUrlEncodedContent 是错误的选择,您应该改用StringContent,类似于:

var content = new StringContent(HttpUtility.UrlEncode(values), Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://URL.com/Token", content);

原因是,我认为 FormUrlEncodeContent 没有接受添加内容类型的重载。

另一种选择是切换到使用 SendAsync 而不是 PostAsync,因为 SendAsync 具有一些额外的灵活性。

【讨论】:

  • 您好,感谢您的建议。我之前尝试过类似的东西。当我使用您上面的示例时,我收到错误:CS1502:'System.Web.HttpUtility.UrlEncode(string)' 的最佳重载方法匹配有一些无效参数
  • 是的,我的错误,我错过了您使用字典来获取值。您必须先将字典展平,或者将其转换为 json 字符串。
猜你喜欢
  • 2020-12-02
  • 2017-07-22
  • 2014-05-10
  • 2019-05-18
  • 1970-01-01
  • 2012-10-27
  • 1970-01-01
  • 2017-06-12
  • 2016-05-20
相关资源
最近更新 更多