“The JSON value could not be converted to System.String” when attempting to call controller endpoint
The model binder is unable to map/bind the sent data to the controller parameters
Your action expects a simple string from the request body
public async Task<ActionResult> AddQuestion([FromBody] string question)
But you sent a complex object
{ "test" : "test" }
You might have gotten a match if the property name(s) had matched
For example
{ "question" : "test" }
Since the model binder will take property names into consideration when matching parameters.
if you want to receive a raw string then you need to send a valid raw JSON string
"{ \"test\": \"test \"}"
That is properly escaped.
Another options is to use a complex object for the parameter
class Question {
public string test { get; set; }
//...other properties
}
that matches the expected data
public async Task<ActionResult> AddQuestion([FromBody] Question question) {
string value = question.test;
//...
}
The model binder will bind the data and pass it to the action parameter(s).
Reference Model Binding in ASP.NET Core
Thanks @Nkosi for identifying the issue and providing the article Model Binding in ASP.NET Core.
Since I spent a lot of time going through outdated examples on how to call an API, here's my code for reference (as of Sep-2020):
On the API project, I used the [BindProperty] attribute on the properties of the model class.
// ASP.NET CORE API - C# model
using Microsoft.AspNetCore.Mvc;
using System;
namespace MyTestAPI.Models
{
public partial class MyTest
{
[BindProperty]
public int TestId { get; set; }
[BindProperty]
public string Message { get; set; }
[BindProperty]
public Guid? CreatedBy { get; set; }
[BindProperty]
public DateTime Timestamp { get; set; }
}
}
On the API controller, the mytest class is automatically deserialized because of the property attributes of the MyTest model class:
// ASP.NET CORE API - C# controller
using Dapper;
using HangVue.API.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MyTestAPI.Models;
namespace HangVue.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpPost]
public void Post([FromBody] MyTest mytest)
{
var parameters = new DynamicParameters();
parameters.Add("@pMessage", mytest.Message, System.Data.DbType.String, System.Data.ParameterDirection.Input);
parameters.Add("@CreatedBy", mytest.CreatedBy.ToString(), System.Data.DbType.String, System.Data.ParameterDirection.Input);
string sql = "[dbo].[uspTest]";
using (var conn = new System.Data.SqlClient.SqlConnection(*** SQL_conn_string_goes_here ***))
{
var affectedRows = conn.Query(sql, parameters, commandType: System.Data.CommandType.StoredProcedure);
}
}
}
}
On the client side, I'm using Xamarin.Forms with RestSharp to invoke my API. The AccessToken is required because I'm using Azure AD B2C authentication.
// Xamarin.Forms - C# Client (iOS + Android)
using Microsoft.Identity.Client;
using RestSharp;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace YourApp.Services
{
public static class TestServices
{
public async static Task<string> NewTest(int iTestId, string sMessage, Guid? gCreatedBy, DateTime dTimestamp)
{
try
{
var mytest = new Models.MyTest
{
TestId = iTestId,
Message = sMessage,
CreatedBy = gCreatedBy,
Timestamp = dTimestamp
};
// Client --- API end-point example: https://yourAPIname.azurewebsites.net/
RestSharp.RestClient client = new RestClient(*** https://Your_API_base_end_point_goes_here ***);
// Request
RestSharp.RestRequest request = new RestSharp.RestRequest("api/test", RestSharp.Method.POST, RestSharp.DataFormat.Json);
request.AddParameter("Authorization", "Bearer " + *** Your_AccessToken_goes_here ***, RestSharp.ParameterType.HttpHeader);
request.AddHeader("Content-Type","application/json; CHARSET=UTF-8");
request.AddHeader("Accept", "application/json");
request.AddJsonBody(mytest);
// Invoke
RestSharp.IRestResponse response = await client.ExecuteAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
*** do something ***
return *** a string *** ;
}
else
{
*** do something ***
return *** a string *** ;
}
}
catch (Exception ex)
{
*** do something ***
}
}
}
}