【问题标题】:Why is my ASP.NET web service unable to complete this POST request?为什么我的 ASP.NET Web 服务无法完成此 POST 请求?
【发布时间】:2020-01-02 05:50:36
【问题描述】:

我有一个内置于 Xamarin.Forms 的数据输入应用程序,旨在将数据捕获为特定格式,然后通过 ASP.NET Web 服务将其发布到 SQL Server 中。我正在使用RestSharp 来简化此操作。以下是我目前必须这样做的代码:

private async void Button_Clicked(object sender, EventArgs e)
        {
            try
            {
                string userResponse = await DisplayActionSheet("Are you sure?", "Yes", "No");
                if (userResponse == "Yes")
                {   
                    using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation))
                    {
                        List<DataToBeSent> d = conn.Query<DataToBeSent>("select * from DataToBeSent");
                        var dConvert = JsonConvert.SerializeObject(d, Formatting.Indented);
                        var client = new RestClient("url here");
                        client.Timeout = -1;
                        var request = new RestRequest(Method.POST);
                        request.RequestFormat = DataFormat.Json;
                        Token token = conn.Query<Token>("select * from Token").FirstOrDefault();
                        request.AddHeader("Authorization", "Bearer " + token.access_token);
                        request.AddParameter("application/json", dConvert, ParameterType.RequestBody);
                        IRestResponse response = client.Execute(request);
                        await DisplayAlert("", response.StatusCode.ToString() + response.Content.ToString(), "OK");
                    }
                    await Navigation.PopToRootAsync();
                }
            }
            catch (Exception ex)
            {
                await DisplayAlert("Error", $"Data failed to be inserted.\n{ex}", "OK");
            }
            
        }

当我尝试提交任何数据时,它会返回以下警报:

Token 类使用成功的 POST 请求来检索 OAuth 令牌,以便应用在启动时使用 GET 数据。

我还使用 Postman 进行了测试,示例请求如下:

这会导致我的网络服务出现以下错误:

值得注意的是,我要发布数据的表没有主键。很遗憾,但要求它没有主键。 表现在有一个主键(名为UniqueID。我还注意到List&lt;DataToBeSent&gt; 在序列化时返回转义字符。为什么会出现这些错误?有谁知道怎么解决?

更新 1: 应要求; JSON 传递如下。

"[\n  {\n    \"column1\": \"data1\",\n    \"column2\": \"data2\"\n  }\n]"

数据库模型代码如下。

<!--Errors Found During Generation:
warning 6002: The table/view 'databaseName.dbo.DataToBeSent' does not have a primary key defined. The key has been inferred and the definition was created as a read-only table/view.-->
        <EntityType Name="DataToBeSent">
          <Key>
            <PropertyRef Name="column1" />
            <PropertyRef Name="column2" />
          </Key>
          <Property Name="column1" Type="nvarchar" MaxLength="50" Nullable="false" />
          <Property Name="column2" Type="nvarchar" MaxLength="50" Nullable="false" />
        </EntityType>
<EntitySet Name="DataToBeSent" EntityType="Self.DataToBeSent" store:Type="Tables" store:Schema="dbo">
            <DefiningQuery>
              SELECT
              [DataToBeSent].[column1] AS [column1],
              [DataToBeSent].[column2] AS [column2]
            </DefiningQuery>
          </EntitySet>

更新 2: 我已尝试在 Xamarin 后端更新我的 POST 请求代码。这是我目前拥有的。

List<DataToBeSent> DATA = conn.Query<DataToBeSent>("select * from DataToBeSent");
                        var data = JsonConvert.SerializeObject(DATA, Formatting.None);
                        Token token = conn.Query<Token>("select * from Token").FirstOrDefault();
                        var client = new RestClient("url");
                        client.Timeout = -1;
                        var request = new RestRequest(Method.POST);
                        request.AddHeader("Content-Type", "application/json");
                        request.AddHeader("Authorization", "Bearer " + token.access_token);
                        request.AddJsonBody(data);
                        IRestResponse response = client.Execute(request);
                        await DisplayAlert("Response", response.StatusCode + response.Content, "OK");

我不知道为什么会发生序列化错误 - 在 Visual Studio 中将结果作为JSON 对象查看时,转义字符不存在。这将向我表明序列化正在正确发生,但我仍然收到相同的错误消息(BadRequest)。我正在尝试提交对象的List&lt;&gt;,所以我想知道JsonConvert.SerializeObject 是否是正确的使用方法。

更新 3: 我已尝试按照下面@Mat J 的建议发布单个对象。代码与更新 2 相同,只是将 List&lt;&gt; 对象更改为带有 .FirstOrDefault() 的单个对象。见下文。

DataToBeSent DATA = conn.Query<DataToBeSent>("select * from DataToBeSent").FirstOrDefault();
var data = JsonConvert.SerializeObject(DATA, Formatting.None);

应用中显示的响应。

以下是来自网络服务的控制器代码。我将其自动生成为Web API 2 Controller, using Entity Framework,并在其上方添加方括号以使用我的 OAuth 安全方法。

[Authorize(Roles = "Admin, User")]
[HttpPost]
[Route("api/v/DataToBeSent")]
[ResponseType(typeof(DataToBeSent))]
public IHttpActionResult PostDataToBeSent(DataToBeSent dataToBeSent)
 {
      if (!ModelState.IsValid)
      {
          return BadRequest(ModelState);
      }

      db.DataToBeSent.Add(dataToBeSent);

      try
      {
          db.SaveChanges();
      }
      catch (DbUpdateException)
      {
          if (DataToBeSentExists(dataToBeSent.UniqueID))
          {
              return Conflict();
          }
          else
          {
              throw;
          }
      }

      return CreatedAtRoute("DefaultApi", new { id = dataToBeSent.UniqueID }, dataToBeSent);
  }

DataToBeSent现在有一个主键,名为 UniqueID,如上所述。

我确实希望能够提交整个 List&lt;&gt; 对象,而不是执行类似 for 循环的操作并为列表中的每个单独对象单独调用 POST

【问题讨论】:

  • 第一个错误消息非常清楚 - 它需要一个对象并且您正在传递一个数组。它甚至提供了如何修复它的建议。但是您还没有向我们展示 1) 模型的代码,2) 正在传递的 json,以及 3) Web 服务的签名,因此无法告诉您可能需要更改什么
  • 第二个错误似乎是 EF 问题。谷歌搜索错误消息会返回数百个点击,其中一些来自之前关于 SO 的问题
  • 添加1)和2),如何找到webservice的签名? @Jason 是的,我尝试了几种解决方案,例如删除 DefiningQuery 并将 store:Schema 更新为仅 Schema,这两种方法都不适合我。
  • 如果您还不清楚,则在 Web 服务中生成错误。您正在发送一个数组,Web 服务具有类似于 public ActionResult ApiEndpoint(DataToBeSent model) 的签名,而您像 ApiEndpoint(listOfDataToBeSent) 一样调用它会导致错误。通过在查询末尾添加 FirsOrDefault 调用来发送一个对象。
  • 请在最近的编辑中查看FirstOrDefault 的测试结果。

标签: c# asp.net xamarin json.net restsharp


【解决方案1】:

我建议阅读 RestSharp 文档。我们试图让它更易于使用,我不知道你为什么不从中受益。

让我们看看这段代码:

var data = JsonConvert.SerializeObject(DATA, Formatting.None);
Token token = conn.Query<Token>("select * from Token").FirstOrDefault();
var client = new RestClient("url");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + token.access_token);
request.AddJsonBody(data);
IRestResponse response = client.Execute(request);
  • 首先,AddJsonBody 接受 object,它会为您序列化。您正在发送一个 string,它已经包含一个序列化对象,所以这永远不会工作。

  • 通过调用AddJsonObject,您已经将内容类型设置为application/json,无需再次设置。

  • 您可以只使用为身份验证而构建的JwtAuthenticator,而不是手动设置不记名标头,而无需为每个请求操作标头。

  • 建议客户端为单例,不要为每个请求重新创建。

我可以建议使用以下方法:

public void WhateverIsCalledOnce()
{
    var roken = conn.Query<Token>("select * from Token").First();
    _client = new RestClient("url").UseNewtonsoftJson();
    _client.Authenticator = new JwtAuthenticator(token.access_token);
}

public async Task CallRemoteServer()
{
    var data = await _connection.GetDataFromDb("query");
    var request = new RestRequest("/api/v/DataToBeSent")
        .AddJsonBody(data);
    var result = await _client.PostAsync<DataToBeReturned>(request);
}

UseNewtonsoftJson 扩展方法在 NuGet 上的 RestSharp.Serializers.NewtonsoftJson 包中可用。

【讨论】:

    【解决方案2】:

    我解决了这个问题,将其发布在此处以供将来遇到问题的任何人使用。正如@Mat J 在问题的 cmets 中指出的那样,当控制器仅设置为接收单个对象时,我试图发送一个数组。第 1 步是更新该代码以允许控制器接收 List&lt;&gt; 而不是单个对象。第 2 步意识到我将 POST 控制器的 [Route] 属性设置为与表的 GET 控制器相同。可以在问题的 Update 2 中找到工作的 Xamarin 端代码,尽管@Alexey Zimarev 的答案可能会更有效。更新的 Web 服务控制器代码如下所示。

    [Authorize(Roles = "Admin, User")]
    [HttpPost]
    [Route("api/postall/datatobesent")]
    public IHttpActionResult PostAllDataToBeSent([FromBody] List<DataToBeSent> dataToBeSent)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        foreach(var data in dataToBeSent)
        {
            db.DataToBeSent.Add(data);
        }
    
        try
        {
            db.SaveChanges();
    
            return Ok();
        }
        catch (Exception)
        {
            throw;
        }
    
    }
    

    感谢所有评论和贡献的人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-17
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-19
      相关资源
      最近更新 更多