I have a Backbone.js application and it's calling one of my WEB API custom POST method. Here is the code to my Custom POST
WebApiConfig.cs
config.Routes.MapHttpRoute
(
name: "UserAdministrationApi",
routeTemplate: "api/{controller}/PostUserInfo/{EmployeeDTO}",
defaults: new
{
EmployeeDTO = RouteParameter.Optional,
controller = "UserAdministration",
action = "PostUserInfo",
});
Controller
[HttpPost]
[ActionName("PostUserInfo")]
public HttpResponseMessage PostUserInfo(EmployeeDTO value)
{
// handling POST
}
EmployeeDTO
public class EmployeeDTO
{
public bool masAccess { get; set; }
public bool reportAccess { get; set; }
public string name { get; set; }
public string office { get; set; }
}
When I try to test this in Fiddler even before going to test it with Backbone.js code, I am getting 500 Internal error. Not sure what's wrong
// FIDDLER TEST
POST : http://localhost:56501/api/useradministration/PostUserInfo
Request Body
{
masAccess:"true"
reportAccess:"false"
}
Thank you for the help
A few things:
The EmployeeDTO should not be part of the routeTemplate, as Andrei mentioned.
Your request body is not valid JSON according to JSONlint.com. Try
{
"masAccess": "true",
"reportAccess": "false"
}
Also, you may need to add the header Content-Type: application/json to your Fiddler request.
Related
I'd like to be able to POST data like this to a REST API:
POST /foo/b HTTP/1.1
Accept: application/json
Content-Type: application/json
{ "Qux": 42, "Corge": "c" }
The URL segment after foo (i.e. b) also contains data that I need to capture in a server-side variable. I've tried to implement this feature in ServiceStack (see code below), but the response body is null.
Here's first the request type:
[Route("/foo/{Bar}", "POST")]
public class PostFooRequest : IReturn<PostFooResponse>
{
public string Bar { get; set; }
[ApiMember(ParameterType = "body")]
public Foo Body { get; set; }
}
As you can see, Bar is a URL variable. The Foo class is defined like this:
public class Foo
{
public int Qux { get; set; }
public string Corge { get; set; }
}
Furthermore, the response looks like this:
public class PostFooResponse
{
public string Bar { get; set; }
public Foo Foo { get; set; }
}
Finally, the service itself is defined like this:
public class ReproService : Service
{
public object Post(PostFooRequest request)
{
return new PostFooResponse { Bar = request.Bar, Foo = request.Body };
}
}
Notice that this method simply echoes the values of the request in the response.
When I execute the above request, I only get the Bar value back:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"bar":"b"}
Setting a breakpoint in the Post method reveals that request.Body is null.
How do I write the code so that the API has the desired contract?
FWIW, I'm aware of this question, but the answer only explains what the problem is; not how to solve it.
If you would translate your current request to the following DTO the serializer should be able to fill the properties:
[Route("/foo/{Bar}", "POST")]
public class PostFooRequest : IReturn<PostFooResponse>
{
public string Bar { get; set; }
public int Qux { get; set; }
public string Corge { get; set; }
}
The serializer has no way to know how to deserialize the object you're sending.
Looking at your DTO and the request I would expect a different request.
POST /foo/b HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"Foo": { "Qux": 42, "Corge": "c" }
}
Other way of retrieving the FormData would be using the following property in your Servicestack service
Request.FormData. Make sure you're not calling the DTO but capital Request.
I'm running server build with asp.net core (v2.1) web api and have this REST HTTP call:
POST http://website.com/callback
with the headers:
...
Content-Type: application/x-www-form-urlencoded
and the body:
response%5Bkey%5D=123&response%5Bname%5D=hi
I want to receive this message at this point:
[HttpPost]
[Route("callbacks")]
public ActionResult Hook([FromForm]Model model)
{
// <---- Model has instance but empty fields
return Ok();
}
My Model is:
public class Model
{
public string key { get; set; }
public string name { get; set; }
}
Somehow the brackets ("[key]" and "[name]") are not being parsed into my model instance. They are null both, although I provide them in body.
How to solve it?
You should set name in form for your properties:
public class Model
{
[FromForm(Name = "response[key]")]
public string key { get; set; }
[FromForm(Name = "response[name]")]
public string name { get; set; }
}
And I wrote custom action names for ever process.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In webapi config I wrote like this and in controller, I started like below
[System.Web.Http.HttpPost]
public HttpResponseMessage Control(List<string>codes,string updateperson,string refPeriod, int actualyear, int actualmonth)
{
For get methods, everything works well but for post method it doesn't work and gives error like below.
In body in post I send
{codes: ["CPM2018-004"], updateperson: "E3852", refPeriod: "SFC18", actualyear: 2018, actualmonth: 12}
Request URL:http://localhost:50941/Api/Investment/Control Request
Method:POST Status Code:404 Not Found Remote Address:[::1]:50941
Referrer Policy:no-referrer-when-downgrade
How can I sreceive post requests to web API with custom action name?
Create model to hold value being posted.
public class ControlViewModel {
public List<string> codes { get; set; }
public string updateperson { get; set; }
public string refPeriod { get; set; }
public int actualyear { get; set; }
public int actualmonth { get; set; }
}
And then update the action to expect the data in the BODY of the request
public class InvestmentController : ApiController {
[HttpPost]
public HttpResponseMessage Control([FromBody]ControlViewModel data) {
//...
}
//...
}
Reference Parameter Binding in ASP.NET Web API
I've read a few SO posts and none of them quite cover my scenario so I'm going to post here.
Given the following route config registration:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and these controller actions in a controller that inherits from ApiController:
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request)
{
}
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request)
{
}
public class GetDocumentsRequest
{
public string CorrelationId { get; set; }
public int Id { get; set; }
public string ObjectId { get; set; }
public string BusinessArea { get; set; }
public string UserId { get; set; }
public string SystemName { get; set; }
public string SystemToken { get; set; }
public Letter LetterDetails { get; set; }
public List<KeyValuePair<string, string>> KeyValue { get; set; }
}
public class FinishDocumentsRequest
{
public string CorrelationId { get; set; }
public string[] Documents { get; set; }
}
I thought doing it this way would be enough disambiguation for the IHttpActionSelector to correctly choose the route, but unfortunately it is not.
So my questions is "Is there a way to make this code work correctly, and keep it in the same controller?"
Thank you,
Stephen
You could use attribute routing for this.
Define the route as a string in the Route attribute ontop of the methods as this
[Route("api/controller/Post1")]
[HttpPost]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request)
{
}
[Route("api/controller/Post2")]
[HttpPost]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request)
{
}
The request routing pipeline isn't smart enough to determine if the body of the request matches the parameter type (aka overloading). (The compiler is smart enough, which is why this compiles and you have runtime issues.)
You have a couple of different options.
You can either add an [Route(<ActionName>)] attribute on both of your posts.
Make two controllers, one for GetDocuments and one for FinishDocuments
Make one Post method that is ambiguous. (I'd avoid this)
If you choose option 1, your API uri will have to be .../api/MyController/MyActionName rather than .../api/MyController/. It's also advisable to add [HttpGet] and [HttpPost] attributes on your methods.
Sample:
public class DocumentController : ApiController
{
// POST /api/Document/GetDocuments
[HttpPost]
[Route("GetDocuments")]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request) { ... }
// POST /api/Document/FinishDocuments
[HttpPost]
[Route("FinishDocuments")]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request){ ...}
}
If you choose option 2, you have to maintain an additional code file.
public class GetDocumentsController : ApiController
{
// POST /api/GetDocuments
[HttpPost]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request) { ... }
}
public class FinishDocumentsController : ApiController
{
// POST /api/FinishDocuments/
[HttpPost]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request){ ...}
}
If you choose option 3, may God have mercy on your soul you're going to have a bad time maintaining it.
Add the Route attribute decoration to your web api functions and that will assit the selector to choose the route:
[Route("Post1")]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request)
{
}
[Route("Post2")]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request)
{
}
I also recommend adding the http method decoration such as [HttpPost] or [HttpGet]
I'm trying to pass a JSON array to an ApiController but the string values aren't deserializing (they are set to null values). The strange thing is that I still get the correct number of elements.
A have an ApiController:
[RoutePrefix("api/language")]
public class LanguagePairApiController : ApiController
With a post method:
// POST: api/language/create
[HttpPost]
[Route("create")]
public string Create([FromBody]LanguagePair[] languagePairs)
I'm sending JSON to it:
[
{"Key":"Test","Value":"Test","Version":"1.0"},
{"Key":"Areyousure","Value":"Are you sure?","Version":"1.0"},
{"Key":"File","Value":"File","Version":"1.0"}
]
And this is the class I'm trying to map it to:
public class LanguagePair
{
public string Key { get; set; }
public string Value { get; set; }
public string Version { get; set; }
}
But the string values are coming through as null:
What am I missing?
EDIT: I've figured out one answer to this and posted it below. But I'm still looking for a better answer...
I figured it out. I needed to decorate the class with DataContract and DataMember attributes:
{
[DataContract]
public class LanguagePair
{
[DataMember]
public string Key { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember]
public string Version { get; set; }
}
}
Read Parameter Binding in ASP.NET Web API
You need to remove [FromBody] attribute from your action...
// POST: api/language/create
[HttpPost]
[Route("create")]
public string Create(LanguagePair[] languagePairs) { ... }
and you can keep your class lean as you originally had it:
public class LanguagePair
{
public string Key { get; set; }
public string Value { get; set; }
public string Version { get; set; }
}
Using [FromBody]
To force Web API to read a simple type from the request body, add the
[FromBody] attribute to the parameter:
public HttpResponseMessage Post([FromBody] string name) { ... }
In this example, Web API will use a media-type formatter to read the
value of name from the request body. Here is an example client
request.
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"
When a parameter has [FromBody], Web API uses the Content-Type
header to select a formatter. In this example, the content type is
"application/json" and the request body is a raw JSON string (not a
JSON object).