I have registered the Batch WebAPI and WebAPI service as follows in webapiconfig.cs
config.Routes.MapHttpBatchRoute(
routeName: "WebApiBatch",
routeTemplate: "api/$batch",
batchHandler: new DefaultHttpBatchHandler(GlobalConfiguration.DefaultServer));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And webAPI service as follows
Employee Class
public class Employee
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Employee()
{
}
public Employee(int id, string LN, string FN)
{
this.EmployeeID = id;
this.LastName = LN;
this.FirstName = FN;
}
}
Employee Interface
interface IEmployeeRepository
{
IEnumerable<Employee> GetAll();
Employee Get(int EmployeeID);
Employee Add(Employee emp);
void Remove(int EmployeeID);
bool Update(Employee emp);
}
Employee Repository
public class EmployeeRepository : IEmployeeRepository
{
private List<Employee> emp = new List<Employee>();
public EmployeeRepository()
{
emp.Add(new Employee(1, "Davolio", "Nancy"));
emp.Add(new Employee(2, "Fuller", "Andrew"));
emp.Add(new Employee(3, "Leverling", "Janet"));
emp.Add(new Employee(4, "Peacock", "Margaret"));
emp.Add(new Employee(5, "Buchanan", "Steven"));
}
public IEnumerable<Employee> GetAll()
{
return emp;
}
public Employee Get(int id)
{
return emp.Find(p => p.EmployeeID == id);
}
public Employee Add(Employee eObj)
{
if (eObj == null)
{
throw new ArgumentNullException("eObj");
}
emp.Add(eObj);
return eObj;
}
public void Remove(int id)
{
emp.RemoveAll(p => p.EmployeeID == id);
}
public bool Update(Employee eObj)
{
if (eObj == null)
{
throw new ArgumentNullException("eObj");
}
int index = emp.FindIndex(p => p.EmployeeID == eObj.EmployeeID);
if (index == -1)
{
return false;
}
emp.RemoveAt(index);
emp.Add(eObj);
return true;
}
}
WebAPI controller
public class EmployeeController : ApiController
{
static readonly IEmployeeRepository repository = new EmployeeRepository();
// GET api/<controller>
[HttpGet]
public object Get()
{
var queryString = HttpContext.Current.Request.QueryString;
var data = repository.GetAll().ToList();
return new { Items = data, Count = data.Count() };
}
public Employee GetEmployee(int id)
{
Employee emp = repository.Get(id);
if (emp == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return emp;
}
// POST api/<controller>
public HttpResponseMessage PostEmployee(Employee emp)
{
emp = repository.Add(emp);
var response = Request.CreateResponse<Employee>(HttpStatusCode.Created, emp);
string uri = Url.Link("Employee", new { id = emp.EmployeeID });
response.Headers.Location = new Uri(uri);
return response;
}
[HttpPut]
// PUT api/<controller>
public void PutEmployee(Employee emp)
{
if (!repository.Update(emp))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
[HttpDelete]
public void Delete(int id)
{
// int empID = int32
Employee emp = repository.Get(id);
if (emp == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.Remove(id);
}
}
I have raised the ajax request from the client, in order to access the service as follows
I have set the batch request in data field of ajax
--batch_ec79f662-862e-4016-a19a-1dbff86d7120
Content-Type: multipart/mixed; boundary=changeset_eb327bfc-5424-47b6-becf-416c43e13899
--changeset_eb327bfc-5424-47b6-becf-416c43e13899
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /api/Employee HTTP/1.1
Content-Id: 0
Content-Type: application/json; charset=utf-8
{"EmployeeID":6,"FirstName":"angel","LastName":"dsfs"}
--changeset_eb327bfc-5424-47b6-becf-416c43e13899
Content-Type: application/http
Content-Transfer-Encoding: binary
PUT /api/Employee HTTP/1.1
Content-Id: 1
Content-Type: application/json; charset=utf-8
{"EmployeeID":2,"FirstName":"kalai","LastName":"selvi"}
--changeset_eb327bfc-5424-47b6-becf-416c43e13899
Content-Type: application/http
Content-Transfer-Encoding: binary
PUT /api/Employee HTTP/1.1
Content-Id: 2
Content-Type: application/json; charset=utf-8
{"EmployeeID":3,"FirstName":"Janet","LastName":"Leverling"}
--changeset_eb327bfc-5424-47b6-becf-416c43e13899
Content-Type: application/http
Content-Transfer-Encoding: binary
DELETE /api/Employee(3) HTTP/1.1
Content-Id: 3
Content-Type: application/json; charset=utf-8
--changeset_eb327bfc-5424-47b6-becf-416c43e13899--
--batch_ec79f662-862e-4016-a19a-1dbff86d7120--
And the entire ajax request
$.ajax{
Content-type: "multipart/mixed; charset=UTF-8;boundary=batch_ec79f662-862e-4016-a19a-1dbff86d7120",
data:"--batch_ec79f662-862e-4016-a19a-1dbff86d7120↵Content-Type: multipart/mixed; boundary=changeset_eb327bfc-5424-47b6-becf-416c43e13899↵↵--changeset_eb327bfc-5424-47b6-becf-416c43e13899↵Content-Type: application/http↵Content-Transfer-Encoding: binary ↵↵POST /api/Employee HTTP/1.1↵Content-Id: 0↵Content-Type: application/json; charset=utf-8 ↵↵{"EmployeeID":6,"FirstName":"angel","LastName":"dsfs"}↵↵--changeset_eb327bfc-5424-47b6-becf-416c43e13899↵Content-Type: application/http↵Content-Transfer-Encoding: binary ↵↵PUT /api/Employee HTTP/1.1↵Content-Id: 1↵Content-Type: application/json; charset=utf-8 ↵↵{"EmployeeID":2,"FirstName":"kalai","LastName":"selvi"}↵↵↵--changeset_eb327bfc-5424-47b6-becf-416c43e13899↵Content-Type: application/http↵Content-Transfer-Encoding: binary ↵↵PUT /api/Employee HTTP/1.1↵Content-Id: 2↵Content-Type: application/json; charset=utf-8 ↵↵{"EmployeeID":3,"FirstName":"Janet","LastName":"Leverling"}↵↵↵--changeset_eb327bfc-5424-47b6-becf-416c43e13899↵Content-Type: application/http↵Content-Transfer-Encoding: binary ↵↵DELETE /api/Employee(3) HTTP/1.1↵Content-Id: 3↵Content-Type: application/json; charset=utf-8 ↵↵--changeset_eb327bfc-5424-47b6-becf-416c43e13899--↵--batch_ec79f662-862e-4016-a19a-1dbff86d7120--",
type:"POST",
url:"/api/Employee"
}
After raise the request, I got an exception message
1. {Message: "The request entity's media type 'multipart/mixed' is not supported for this resource.",…}
1. ExceptionMessage:"No MediaTypeFormatter is available to read an object of type 'Employee' from content with media type 'multipart/mixed'."
2. ExceptionType:"System.Net.Http.UnsupportedMediaTypeException"
3. Message:"The request entity's media type 'multipart/mixed' is not supported for this resource."
StackTrace:" at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
↵ at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
↵ at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)"
Where did i commit the mistake
Make sure that you are calling the actual batch route that you configured and not an endpoint on the EmployeeController directly. When you do that (I see your ajax call has url:"/api/Employee") then you will get that message since those endpoints don't support mixed mode. If you call the batch route instead (/api/batch), then that should do the trick. The endpoint I used didn't have the dollar sign in it however, I configured it to just be api/batch:
routeName: "batch",
routeTemplate: "api/batch",
Related
I want to mimic behaviour of existing web service. Here is a very simplified example showing what I want to achieve.
I use ASP.Net Web API routing: it's quite simple to configure routes with it.
Requirements, part 1: query:
GET whatever.../Person/1
shall return JSON:
Content-Type: application/json; charset=utf-8
{"id":1,"name":"Mike"}
That's piece of cake:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
}
// In ApiController
[HttpGet]
[Route("Person/{id}")]
public Person GetPerson(int id)
{
return new Person
{
ID = id,
Name = "Mike"
};
}
Requirements, part 2: query:
GET whatever.../Person/1?callback=functionName
shall return javascript:
Content-Type: text/plain; charset=utf-8
functionName({"id":1,"name":"Mike"});
Any ideas how to achieve this (part 2)?
The ApiController would need to be modified to satisfy the desired behavior
Simple example based on provided code
//GET whatever.../Person/1
//GET whatever.../Person/1?callback=functionName
[HttpGet]
[Route("Person/{id:int}")]
public IHttpActionResult GetPerson(int id, string callback = null) {
var person = new Person {
ID = id,
Name = "Mike"
};
if (callback == null) {
return Ok(person); // {"id":1,"name":"Mike"}
}
var response = new HttpResponseMessage(HttpStatusCode.OK);
var json = JsonConvert.SerializeObject(person);
//functionName({"id":1,"name":"Mike"});
var javascript = string.Format("{0}({1});", callback, json);
response.Content = new StringContent(javascript, Encoding.UTF8, "text/plain");
return ResponseMessage(response);
}
Of course you would need to do proper validation on the call back as this currently open up the API for script injection.
I want to receive form data from Postman:
Content-Type: application/json
Here is WebApi method:
[HttpPost]
[Route("api/test")]
public async Task TestMethod(HttpRequestMessage request)
{
var test = await request.Content.ReadAsStringAsync();
}
What I'm getting is:
------WebKitFormBoundarypqDvmeG89cBR9mK9
Content-Disposition: form-data; name="test"
esad
------WebKitFormBoundarypqDvmeG89cBR9mK9--
But I don't want data with WebKitFormBoundary and I've restriction to use formdata only. Is there any other way?
HTTP call information:
POST /api/test HTTP/1.1
Host: localhost:16854
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: 1a3d6427-4956-707d-da0c-3a29a63c7563
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="test"
esad
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Curl call information:
curl -X POST \
http://localhost:16854/api/test \
-H 'cache-control: no-cache' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-H 'postman-token: 02055873-e9a8-e9a6-019c-b407992b0e2f' \
-F test=esad
1) If you have to send Content-Type: multipart/form-data OR simply form-data
This is the first tab of Postman
If you have to collect only one key/value pair of your posted form-data
[HttpPost]
[Route("api/test")]
public HttpResponseMessage TestMethod(HttpRequestMessage request)
{
var testValue = HttpContext.Current.Request.Form["test"];
return Request.CreateResponse(HttpStatusCode.OK, testValue);
}
If you have to collect more than one key/value pair of your posted form-data
[HttpPost]
[Route("api/test")]
public HttpResponseMessage TestMethod(HttpRequestMessage request)
{
NameValueCollection collection = HttpContext.Current.Request.Form;
var items = collection.AllKeys.SelectMany(collection.GetValues, (k, v) => new { key = k, value = v });
//We just collect your multiple form data key/value pair in this dictinary
//The following code will be replaced by yours
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
foreach (var item in items)
{
keyValuePairs.Add(item.key, item.value);
}
return Request.CreateResponse(HttpStatusCode.OK, keyValuePairs);
}
2) If you have to send Content-Type: application/x-www-form-urlencoded
This is the second tab of Postman
Then your API will be
[HttpPost]
[Route("api/test")]
public async Task TestMethod(HttpRequestMessage request)
{
var test = await request.Content.ReadAsFormDataAsync();
}
Then you will get following output when you debug your code with breakpoint
3) If you have to send Content-Type: application/json
This is the third tab of Postman
See below screenshot for such option
And your api is
[HttpPost]
[Route("api/test")]
public async Task TestMethod(HttpRequestMessage request)
{
var jObject = await request.Content.ReadAsAsync<JObject>();
Item item = JsonConvert.DeserializeObject<Item>(jObject.ToString());
}
And your model to collect this posted data
public class Item
{
public string test { get; set; }
}
And your output will be
The advantage of this option you can send complex type as posted data and like
And your api is
[HttpPost]
[Route("test")]
public async Task TestMethod(HttpRequestMessage request)
{
var jObject = await request.Content.ReadAsAsync<JObject>();
Sample sample = JsonConvert.DeserializeObject<Sample>(jObject.ToString());
}
And you model to collect this data are
public class Item
{
public string test { get; set; }
}
public class Sample
{
public Item item { get; set; }
}
And you will see the output is
The following code will read key/value correctly when sent from Postman with form-data option selected
[HttpPost]
[Route("api/test")]
public async Task<HttpResponseMessage> TestMethod(HttpRequestMessage request)
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
var testValue = provider.FormData.GetValues("test")[0];
return Request.CreateResponse(HttpStatusCode.OK);
}
A more thorough example can be found here (Section: Reading Form Control Data).
Edit: The HTTP call that is sent to the above API handler is the one below:
POST /api/stats/testmethod HTTP/1.1
Host: localhost:4100
Cache-Control: no-cache
Postman-Token: 999fd13d-f804-4a63-b4df-989b660bcbc5
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="test"
esad
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Below statement can do this:
NameValueCollection form = HttpContext.Current.Request.Form;
This is a follow up to the questions Custom form data with multiple files to Web API controller and Filling Parameter with Attribute [FromForm] with Fiddler, which are both answered by citing A MediaTypeFormatter for WebApi for multipart/form-data including file uploads.
Essentially this is the same question Tocana has asked before in the last link.
I copied the code from the latter, registered the media type formatter and tried it with fiddler, but as it enters UploadController.Post(UploadRequestViewModel model) model is instanciated but its attributes are all equal to null.
I debugged it and found that FormMultipartEncodedMediaTypeFormatter.BindToModel(IDictionary<string, object> data, Type type, IFormatterLogger formatterLogger) is called with a correct data dictionary, but from then on I cannot make head from tails. The binder used to bind the model is CompositeModelBinder, but wether that is correct or not I cannot tell.
The fiddler request looks like this:
Header:
Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
Host: localhost:49473
Content-Length: 13192
Body
---------------------------acebdf13572468
Content-Disposition: form-data; name="SiteId"
Content-Type: text/plain
987654321
---------------------------acebdf13572468
Content-Disposition: form-data; name="StartDate"
Content-Type: text/plain
24.08.1979
---------------------------acebdf13572468
Content-Disposition: form-data; name="Zulu"; filename="ExifTest.tif"
Content-Type: image/tiff
<#INCLUDE *C:\...\ExifTest.tif*#>
---------------------------acebdf13572468--
What do I have to do to get my model correctly initialized, i.e. its values are not null?
EDIT:
All models and controllers are those of A MediaTypeFormatter for WebApi for multipart/form-data including file uploads, i.e.
Model:
public class UploadRequestViewModel
{
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public HttpPostedFileBase File { get; set; }
}
Controller:
[Route("UploadTest")]
[HttpPost]
public IHttpActionResult Post(UploadRequestViewModel model)
{
}
FormMultipartEncodedMediaTypeFormatter.BindToModel
private object BindToModel(IDictionary<string, object> data, Type type, IFormatterLogger formatterLogger)
{
if (data == null) throw new ArgumentNullException(nameof(data));
if (type == null) throw new ArgumentNullException(nameof(type));
using (var config = new HttpConfiguration())
{
// if there is a requiredMemberSelector set, use this one by replacing the validator provider
var validateRequiredMembers = RequiredMemberSelector != null && formatterLogger != null;
if (validateRequiredMembers)
{
config.Services.Replace(typeof(ModelValidatorProvider), new RequiredMemberModelValidatorProvider(RequiredMemberSelector));
}
// create a action context for model binding
var actionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Configuration = config,
ControllerDescriptor = new HttpControllerDescriptor
{
Configuration = config
}
}
};
// create model binder context
var valueProvider = new NameValuePairsValueProvider(data, CultureInfo.InvariantCulture);
var metadataProvider = actionContext.ControllerContext.Configuration.Services.GetModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForType(null, type);
var modelBindingContext = new ModelBindingContext
{
ModelName = string.Empty,
FallbackToEmptyPrefix = false,
ModelMetadata = metadata,
ModelState = actionContext.ModelState,
ValueProvider = valueProvider
};
// bind model
var modelBinderProvider = new CompositeModelBinderProvider(config.Services.GetModelBinderProviders());
var binder = modelBinderProvider.GetBinder(config, type);
var haveResult = binder.BindModel(actionContext, modelBindingContext);
// log validation errors
if (formatterLogger != null)
{
foreach (var modelStatePair in actionContext.ModelState)
{
foreach (var modelError in modelStatePair.Value.Errors)
{
if (modelError.Exception != null)
{
formatterLogger.LogError(modelStatePair.Key, modelError.Exception);
}
else
{
formatterLogger.LogError(modelStatePair.Key, modelError.ErrorMessage);
}
}
}
}
return haveResult ? modelBindingContext.Model : GetDefaultValueForType(type);
}
}
I have ASP.NET application, with some MVC and WebAPI controllers in there. MVC 5.1 and WebAPI 2.1. There is no tricky configuration at all, the only thing is getting configured is removed XML formatter:
public static class WebApi
{
public static void Configure(HttpConfiguration config)
{
var formatters = config.Formatters;
formatters.Remove(formatters.XmlFormatter);
}
}
There is simple DTO to put data returned from controller action together:
public class TokenResponse
{
public string token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string error { get; set; }
}
And the following code:
[NoHttpResponseCaching]
public class TokenController : ApiController
{
public async Task<HttpResponseMessage> PostAsync(HttpRequestMessage request, TokenRequest tokenRequest)
{
try
{
// do some stuff
var response = new TokenResponse { ... };
return request.CreateResponse(HttpStatusCode.OK, response);
}
catch (TokenRequestValidationException ex)
{
_logger.WriteError(ex);
var response = new TokenResponse { error = ex.ErrorToDisplay };
return request.CreateResponse(HttpStatusCode.BadRequest, response);
}
}
}
The problem is that if everything fine, I have my TokenResponse serialized to JSON, as expected, but if there is exception occurring, execution flow is coming into catch block and response body is equal to "Bad Request", this is the RAW dump of response from Fiddler:
HTTP/1.1 400 Bad Request
Cache-Control: no-store
Pragma: no-cache
Content-Type: text/html
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Wed, 18 Jun 2014 08:25:53 GMT
Content-Length: 11
Bad Request
Tried to return anonymous object having some random named properties instead of using TokenResponse, getting same response:
return request.CreateResponse(HttpStatusCode.BadRequest, new {klhjaoiubf = "kjhaflkjh"});
And I'm stuck at this point trying to understand why I'm not getting my object serialized in response body and how to change it. Anyone, any ideas, why my object is getting ignored when response code is BadRequest?
If have the following Api Controller ... using StrutureMap for the DI ...
using System;
using System.Dynamic;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using IdentityService.Domain;
using IdentityService.Domain.Contracts;
using IdentityService.Domain.Models;
namespace IdentityService.Controllers
{
public class AccountController : ApiController
{
private readonly IRepository<Client> _clientRepository;
private readonly IRepository<RelyingParty> _relyingPartyRepository;
private readonly IRepository<Token> _tokenRepository;
public AccountController(
IRepository<Client> clientRepository,
IRepository<RelyingParty> relyingPartyRepository,
IRepository<Token> tokenRepository)
{
_clientRepository = clientRepository;
_relyingPartyRepository = relyingPartyRepository;
_tokenRepository = tokenRepository;
}
public HttpResponseMessage Post(
[FromBody] dynamic data)
{
dynamic result = new ExpandoObject();
try
{
var clientAuthenticator = new ClientAuthenticator(
_clientRepository,
_relyingPartyRepository,
_tokenRepository);
Token token;
clientAuthenticator.Authenticate(
data.Key,
data.ChecksumValue,
data.Checksum,
data.Name,
data.Password,
out token);
result.Token = token;
}
catch (Exception ex)
{
result.ErrorCode = ex.GetType().ToString();
result.ErrorMessage = ex.GetBaseException().Message;
}
return this.Request.CreateResponse(HttpStatusCode.OK, (ExpandoObject)result);
}
}
}
Using Fiddler, I am make the following post:
POST http://localhost:54029/api/account HTTP/1.1
User-Agent: Fiddler
Host: localhost:54029
Content-Type: "application/json"
Content-Length: 218
{
"Key": "7d42276d3c3954716c672543385b575836472f5d543d7776205627413a",
"ChecksumValue": "127.0.0.1",
"Checksum": "ao4Ei77BaX1/iMZMTAJxWzt4fxc=",
"Name": "USER_NAME",
"Password": "PASSWORD"
}
Any idea why my data would be null? I have tried switching to JObject, with no success. All the examples I have found makes me think this should work.
Here is the complete response:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcY29kZS1tYXR0cnVtYVx0YWxrLWF1dGhlbnRpY2F0aW9uLXNlcnZlclxJZGVudGl0eVNlcnZpY2VcYXBpXGFjY291bnQ=?=
X-Powered-By: ASP.NET
Date: Mon, 27 May 2013 13:59:45 GMT
Content-Length: 137
{"ErrorCode":"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException","ErrorMessage":"Cannot perform runtime binding on a null reference"}
Any help would be much appreciated!
Update
I tried just a simple example, like:
public async Task<dynamic> Post(dynamic data)
{
var body = await Request.Content.ReadAsStringAsync();
return data;
}
The parameter data is still null, but I can see the values in body.
Remove the quotes from "application/json".
Content-Type: application/json
In an MVC 6 controller (which extends from Controller and not ApiController) the following does work (with report being JSON) :
[HttpPost("[action]")]
public void RunReport([FromBody]dynamic report)
{
....
}
Updated: For MVC 5 this is what I use
[HttpPost]
public async Task<HttpResponseMessage> FBLogin(Newtonsoft.Json.Linq.JObject jObject)
{
dynamic data = (dynamic)jObject;
string accessToken = data.accessToken;
...
}
Where the JSON payload is :
'{accessToken: "EAAJF9eVIKOsBABdKVNOLJyfyCnnkrl8mlW2crgZC1NYsDqgq9ZBIZC......" }'
if you make the param from [FromBody] to dynamic, and if its a JSON object (made with JSON.stringify) then you can just use .ToString() to get the string value and you should be OK
public void Post(string token, [FromBody]dynamic value)
{
int userID = db.GetUserIdByToken(token);
db.InsertJson(userID, value.ToString());
}
other definitions is headers: {'Content-Type': 'application/json'},
remove [FromBody] attribute and it should work