为什么我的 ASP.NET 网络服务无法完成此 POST 请求?
Why is my ASP.NET web service unable to complete this POST request?
我在 Xamarin.Forms 中内置了一个数据输入应用程序,旨在将数据捕获为特定格式,然后 post 通过 ASP.NET 网络服务将其捕获到 SQL 服务器。我正在使用 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");
}
}
当我尝试提交任何数据时,returns 会出现以下警告:
Token
class 使用成功的 POST
请求检索 OAuth 令牌,供应用启动时用于 GET
数据。
我还使用 Postman 进行了测试,示例请求如下:
这导致我的网络服务出现以下错误:
值得注意的是,我正在posting数据的table没有主键。它是不幸的是,它没有一个要求。 Table 现在有一个主键(名为 UniqueID
)。我还注意到 List<DataToBeSent>
,在序列化时,returns 带有转义字符。为什么会出现这些错误?有谁知道如何修复它们?
更新 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<>
个对象,所以我想知道 JsonConvert.SerializeObject
是否是要使用的正确方法。
更新 3: 我试过按照下面@Mat J 的建议发布单个对象。除了将 List<>
对象更改为具有 .FirstOrDefault()
的单个对象外,代码与更新 2 相同。见下文。
DataToBeSent DATA = conn.Query<DataToBeSent>("select * from DataToBeSent").FirstOrDefault();
var data = JsonConvert.SerializeObject(DATA, Formatting.None);
应用中显示的响应。
下面是来自 Web 服务的我的控制器代码。我将其自动生成为 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
table现在有一个主键,名称如上UniqueID
。
我确实希望能够提交整个 List<>
个对象,而不是像 for 循环那样对列表中的每个单独对象进行单独的 POST
调用。
我建议阅读 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
,其中已经包含一个序列化的 object,所以这永远不会起作用。
通过调用 AddJsonObject
,您已经将内容类型设置为 application/json
,无需再次设置。
无需使用手动设置的承载 header,您只需使用为身份验证目的而构建的 JwtAuthenticator
,无需操作 headers 对于每个请求。
客户端建议单例,不要每个请求都re-created
我可以建议使用以下内容:
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
包中可用。
我解决了这个问题,将其张贴在这里以供日后遇到问题的任何人使用。正如@Mat J 在问题的评论中指出的那样,当控制器仅设置为接收单个对象时,我试图发送一个数组。第 1 步是更新该代码以允许控制器接收 List<>
而不是单个对象。第 2 步意识到我将 POST
控制器的 [Route]
属性设置为与 table 的 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;
}
}
感谢所有评论和贡献的人。
我在 Xamarin.Forms 中内置了一个数据输入应用程序,旨在将数据捕获为特定格式,然后 post 通过 ASP.NET 网络服务将其捕获到 SQL 服务器。我正在使用 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");
}
}
当我尝试提交任何数据时,returns 会出现以下警告:
Token
class 使用成功的 POST
请求检索 OAuth 令牌,供应用启动时用于 GET
数据。
我还使用 Postman 进行了测试,示例请求如下:
这导致我的网络服务出现以下错误:
值得注意的是,我正在posting数据的table没有主键。它是不幸的是,它没有一个要求。 Table 现在有一个主键(名为 UniqueID
)。我还注意到 List<DataToBeSent>
,在序列化时,returns 带有转义字符。为什么会出现这些错误?有谁知道如何修复它们?
更新 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<>
个对象,所以我想知道 JsonConvert.SerializeObject
是否是要使用的正确方法。
更新 3: 我试过按照下面@Mat J 的建议发布单个对象。除了将 List<>
对象更改为具有 .FirstOrDefault()
的单个对象外,代码与更新 2 相同。见下文。
DataToBeSent DATA = conn.Query<DataToBeSent>("select * from DataToBeSent").FirstOrDefault();
var data = JsonConvert.SerializeObject(DATA, Formatting.None);
应用中显示的响应。
下面是来自 Web 服务的我的控制器代码。我将其自动生成为 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
table现在有一个主键,名称如上UniqueID
。
我确实希望能够提交整个 List<>
个对象,而不是像 for 循环那样对列表中的每个单独对象进行单独的 POST
调用。
我建议阅读 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
,其中已经包含一个序列化的 object,所以这永远不会起作用。通过调用
AddJsonObject
,您已经将内容类型设置为application/json
,无需再次设置。无需使用手动设置的承载 header,您只需使用为身份验证目的而构建的
JwtAuthenticator
,无需操作 headers 对于每个请求。客户端建议单例,不要每个请求都re-created
我可以建议使用以下内容:
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
包中可用。
我解决了这个问题,将其张贴在这里以供日后遇到问题的任何人使用。正如@Mat J 在问题的评论中指出的那样,当控制器仅设置为接收单个对象时,我试图发送一个数组。第 1 步是更新该代码以允许控制器接收 List<>
而不是单个对象。第 2 步意识到我将 POST
控制器的 [Route]
属性设置为与 table 的 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;
}
}
感谢所有评论和贡献的人。