POST JSON with MVC 4 API Controller - ajax

I have this code:
$.ajax({
type: "POST",
url: "/api/slide",
cache: false,
contentType: "application/json; charset=utf-8",
data: '{"Title":"fghfdhgfdgfd"}',
dataType: "json",
An this is my controler:
public class SlideController : ApiController
{
// POST /api/Slide
public void Post(string Title)
{
}
When I run the code and call the /api/Slide, the [Title] has no data and is null.
How do I post JSON to the API controller?
POST http://127.0.0.2:81/api/slide HTTP/1.1
Host: 127.0.0.2:81
Connection: keep-alive
Content-Length: 18
Origin: http://127.0.0.2:81
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1
Content-Type: application/json; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
Referer: http://127.0.0.2:81/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Title=fghfdhgfdgfd

Define a view model:
public class SlideViewModel
{
public string Title { get; set; }
}
then have your controller action take this view model as argument:
public class SlideController : ApiController
{
// POST /api/Slide
public void Post(SlideViewModel model)
{
...
}
}
finally invoke the action:
$.ajax({
type: 'POST',
url: '/api/slide',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ title: "fghfdhgfdgfd" }),
success: function() {
...
}
});
The reason for that is that simple types such as strings are bound from the URI. I also invite you to read the following article about model binding in the Web API.

Ensure the object you are trying to convert to has a default (empty) constructor.
Rule of thumb: If you want to deserialize to an object, you need to make it simple for the objects to be created. These guidelines can help:
All properties that are to be passed around must be public
the object needs to able to be constructed without any parameters.
This JSON string/object for example:
{ Name: "John Doe", Phone: "123-456-7890", Pets: [ "dog", "cat", "snake" ] }
can be converted to an object from the following class:
public class Person {
public string Name { get; set; }
public string Phone { get; set; }
public string[] Pets { get; set; }
}
or this one:
public class Person {
public string Name { get; set; }
public string Phone { get; set; }
public string[] Pets { get; set; }
public Person() {}
public Person(string name, string phone) {
Name = name;
Phone = phone;
}
}
or this one:
public class Person {
public string Name { get; set; }
public string Phone { get; set; }
public string[] Pets { get; set; }
public Person() {}
}
but not this one
public class Person {
public string Name { get; set; }
public string Phone { get; set; }
public string[] Pets { get; set; }
public Person(string name, string phone) {
Name = name;
Phone = phone;
}
}
Now let ASP.NET MVC 4 do the rest
public class PersonController : ApiController
{
// .. other actions
public HttpResponseMessage PostPerson(Person person)
{
if ( null != person)
// CELEBRATE by doing something with your object
else
// BE SAD and throw and exception or pass an error message
}
// .. other actions
}
If your class cannot have a default constructor or if you don't have access to the source code for the class, you can create an adapter class that
has a default constructor
exposes those properties that need to be public
Using the Person class above with no default constructor, an adapter could look like
public class PersonAdapter {
public Person personAdaptee;
public string Name {
get { return personAdaptee.Name; }
set { personAdaptee.Name = value }
}
public string Phone {
get { return personModel.Phone; }
set { personModel.Phone = value; }
}
public string[] Pets {
get { return personAdaptee.Pets; }
set {personAdaptee.Pets = value }
}
public PersonAdapter() {
personAdaptee = new Person("", "", null);
}
}
Now let ASP.NET MVC 4 do the rest
public class PersonController : ApiController
{
// .. other actions
public HttpResponseMessage PostPerson(PersonAdapter person)
{
if ( null != person)
// CELEBRATE by doing something with your object
else
// BE SAD and throw and exception or pass an error message
}
// .. other actions
}

Try this:
$.ajax({
type: "POST",
url: "/api/slide",
data: { Title: "fghfdhgfdgfd" }
});
It is the quotes around the data attribute which are causing this:
i.e >> data: { Title: "fghfdhgfdgfd" }
not >> data: '{ Title: "fghfdhgfdgfd" }'
UPDATE:
Also your controller seems a little strange, although it is hard to tell without seeing your routing, etc.
I would expect to see something more like this:
public class SlideController : ApiController
{
public HttpResponseMessage PostSlide(string Title)
{
// Do your insert slide stuff here....
string uri = Url.Link("DefaultApi", new { id = item.Id });
response.Headers.Location = new Uri(uri);
return response;
}
}
Clearly, you will also need to update the URL in your jQuery too.
Take a look here:
http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api
ANOTHER UPDATE:
It would be usual to create a CLR object to match your Json and use the MVC model binder to bind directly to that. If you don't want to do that you can bind to an object and deserialize into a Dictionary:
// POST api/values
public void Post(object json)
{
Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json.ToString());
var x = values["Title"];
}

Cast the action parameter to FromBody i.e:
public class SlideController : ApiController
{
// POST /api/Slide
public void Post([FromBody]string Title)
{
}
}

$.ajax({
type: 'POST',
url: '/api/slide',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ title: "fghfdhgfdgfd" }),
success: function() {
...
}
});
Controller is
public class SlideController : ApiController
{
// POST /api/Slide
public void Post(string Title)
{
}
Your url is not valid, url must address the action Post in Slide controller
edit your url to url:"~/ControllerName/ActionName" in this context must be Url:"~/Slide/Post"

Related

Mediator Api call failing

I'm trying to make a simple request using mediator and .net core. I'm getting an error that I am not understanding. All I'm trying to do is a simple call to get back a guid.
BaseController:
[Route("api/[controller]/[action]")]
[ApiController]
public class BaseController : Controller
{
private IMediator _mediator;
protected IMediator Mediator => _mediator ?? (_mediator = HttpContext.RequestServices.GetService<IMediator>());
}
Controller:
// GET: api/Customer/username/password
[HttpGet("{username}/{password}", Name = "Get")]
public async Task<ActionResult<CustomerViewModel>> Login(string username, string password)
{
return Ok(await Mediator.Send(new LoginCustomerQuery { Username = username,Password = password }));
}
Query:
public class LoginCustomerQuery : IRequest<CustomerViewModel>
{
public string Username { get; set; }
public string Password { get; set; }
}
View Model:
public class CustomerViewModel
{
public Guid ExternalId { get; set; }
}
Handler:
public async Task<CustomerViewModel> Handle(LoginCustomerQuery request, CancellationToken cancellationToken)
{
var entity = await _context.Customers
.Where(e =>
e.Username == request.Username
&& e.Password == Encypt.EncryptString(request.Password))
.FirstOrDefaultAsync(cancellationToken);
if (entity.Equals(null))
{
throw new NotFoundException(nameof(entity), request.Username);
}
return new CustomerViewModel
{
ExternalId = entity.ExternalId
};
}
This is the exception I am getting:
Please let me know what else you need to determine what could be the issue. Also, be kind I have been away from c# for a while.
Thanks for the info it was the missing DI. I added this
// Add MediatR
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
services.AddMediatR(typeof(LoginCustomerQueryHandler).GetTypeInfo().Assembly);
and we are good to go.

WebApi routing - many POST methods

I have WebAPI 2 application. How can I specify 2 or more POST methods?
I have the following WebApiConfig:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and API Controller:
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
[Route("Post1")]
[HttpPost]
public IQueryable<string> Post1(string str)
{
return null;
}
[Route("Post2")]
[HttpPost]
public IQueryable<string> Post2(int id)
{
return null;
}
}
It works neither I call:
/api/books/post1
nor
/api/books/post2
why and how to solve it?
UPDATE:
Problem is solved, problem was in simple types as parameters. I get 404 error
Message=No HTTP resource was found that matches the request URI
'http://localhost:37406/api/books/post1'.
with request:
POST http://localhost:37406/api/books/post1 HTTP/1.1
User-Agent: Fiddler
Host: localhost:35979
Content-Type: application/json; charset=utf-8
{
"str" : "Fffff"
}
and code:
[Route("Post1")]
[HttpPost]
public HttpResponseMessage Post1(string str)
{
return Request.CreateResponse();
}
[Route("Post2")]
[HttpPost]
public HttpResponseMessage Post2(int id)
{
return Request.CreateResponse();
}
but it works fine with complex type:
[HttpPost]
[Route("Post1")]
public HttpResponseMessage Post1(Book book)
{
return Request.CreateResponse();
}
[HttpPost]
[Route("Post2")]
public HttpResponseMessage Post2(Book book)
{
return Request.CreateResponse();
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Genre { get; set; }
}
Thank you Nkosi
UPDATE 2:
but it works when parameter is marked with [FromBody]
[Route("Post1")]
[HttpPost]
public HttpResponseMessage Post1([FromBody]string str)
{
return Request.CreateResponse();
}
[Route("Post2")]
[HttpPost]
public HttpResponseMessage Post2([FromBody]int id)
{
return Request.CreateResponse();
}
(for complex types it's unnecessary). Logically, but Route error confused :)
Excerpt taken from Attribute Routing in ASP.NET Web API 2
HTTP Methods
Web API also selects actions based on the HTTP method of the request
(GET, POST, etc). By default, Web API looks for a case-insensitive
match with the start of the controller method name. For example, a
controller method named PutCustomers matches an HTTP PUT request.
You can override this convention by decorating the mathod with any the
following attributes:
[HttpDelete]
[HttpGet]
[HttpHead]
[HttpOptions]
[HttpPatch]
[HttpPost]
[HttpPut]
The following example maps the CreateBook method to HTTP POST requests.
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
Example:
public class Book {
public int BookId{get;set;}
public string Title{get;set;}
public string Author{get;set;}
public string Genre{get;set;}
}
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/books
[Route("")]
public IEnumerable<Book> Get() { ... }
// GET api/books/5
[Route("{id:int}")]
public Book Get(int id) { ... }
// POST api/books
[HttpPost]
[Route("")]
public HttpResponseMessage Post1(Book book) { ... }
// POST api/books/alternate
[HttpPost]
[Route("alternate")]
public HttpResponseMessage Post2(Book book) { ... }
}
Sample POST Body for Post1
POST http://localhost:35979/api/books HTTP/1.1
User-Agent: Fiddler
Host: localhost:35979
Content-Type: application/json; charset=utf-8
Content-Length: 80
{
"Title":"Scary Book",
"Author":"John Doe",
"Genre":"Horror"
}
Sample POST Body for Post2
POST http://localhost:35979/api/books/alternate HTTP/1.1
User-Agent: Fiddler
Host: localhost:35979
Content-Type: application/json; charset=utf-8
Content-Length: 85
{
"Title":"Fantastic Book",
"Author":"Jane Doe",
"Genre":"Fantasy"
}

The registered message body readers compatible with the MIME media type are: application/json; charset=UTF-8

I am developing a phonegap application where I need to post JSON data from phonegap to rest web service.
ajax call:
var data = {"name" : "abc"};
$.ajax({
type: "POST",
// url: "http://192.168.3.243:8090/ws/welcome",
url: "http://192.168.3.243:8090/TestExample/rest/welcome",
// url: "http://192.168.3.125:8080/JustInReporter/rest/test",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "application/json",
success: function (response) {
console.log(" **** success ** "+response);
}
});
Rest service:
#Path("/welcome")
public class WelcomeImpl
{
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public WelcomeForm welcome(WelcomeForm welcomeFormObject)
{
WelcomeForm form = new WelcomeForm();
form.title = " Connected ... ";
System.out.println("welcomeFormObject *** "+welcomeFormObject.title);
return form;
}
}
#XmlRootElement
public class WelcomeForm
{
public String title;
public WelcomeForm()
{
title = "";
}
public WelcomeForm(String inTitle){
title = inTitle;
}
}
when I run the application I am getting this error at server side:
SEVERE: A message body reader for Java class com.test.beancls.WelcomeForm, and Java type class com.test.beancls.WelcomeForm, and MIME media type application/json; charset=UTF-8 was not found.
The registered message body readers compatible with the MIME media type are:
application/json; charset=UTF-8 ->
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$App
can you tell me where I am doing wrong.
You should try with a more specific bean. In your JSON, you are sending "name" attribute as a part of the main body. So you should have a matching data structure. This may also be accomplished via annotations but as a start this should suffice
public class WelcomeForm
{
public String name;
public WelcomeForm()
{
title = "";
}
public WelcomeForm(String inTitle){
name = inTitle;
}
public String getName(){ return name;}
public void setName(String name){ this.name = name;}
}
And rest service to accept your data can be:
#Path("/welcome")
public class WelcomeImpl
{
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public WelcomeForm welcome(WelcomeForm welcomeFormObject)
{
WelcomeForm form = new WelcomeForm();
form.title = " Connected ... ";
System.out.println("welcomeFormObject *** "+welcomeFormObject.title);
return form;
}
#POST
#Path("list")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public WelcomeForm welcome(WelcomeForm[] welcomeFormObjects)
{
// the path has changed to /welcome/list just to avoid ambiguity
// do whatever you do with the array of objects
}
}

Validate ajax request with Data annotations

I am trying to use the Data-annotations to Validate an ajax request
var request = $.ajax({
url: "http://localhost:55555/WebService1.asmx/HelloWorld",
type: "POST",
data: JSON.stringify({
request: {
Id: '34',
Value: 'Hello World'
}
}),
contentType: "application/json; charset=utf-8",
dataType: "json"
});
Server side:
/// <summary>
/// Summary description for WebService1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[ScriptService]
public class WebService1 : WebService
{
[WebMethod]
public string HelloWorld(TestClass request)
{
return request.Value;
}
}
public class TestClass
{
[Range(0,10)]
public string Id { get; set; }
[StringLength(2)]
public string Value { get; set; }
}
I was hoping that this would fail since my input parameters doesn't match my required attributes. But instead it works fine and I am able to create the class with my "non valid" parameters
What am I doing wrong?

Spring 3 MVC - Advanced Data Binding - Form Request with List of Simple Objects

I've read through all of the Spring 3 Web docs: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/spring-web.html but have been completely unable to find any interesting documentation on binding more complicated request data, for example, let's say I use jQuery to post to a controller like so:
$.ajax({
url: 'controllerMethod',
type: "POST",
data : {
people : [
{
name:"dave",
age:"15"
} ,
{
name:"pete",
age:"12"
} ,
{
name:"steve",
age:"24"
} ]
},
success: function(data) {
alert('done');
}
});
How can I accept that through the controller? Preferably without having to create a custom object, I'd rather just be able to use simple data-types, however if I need custom objects to make things simpler, I'm fine with that too.
To get you started:
#RequestMapping("/controllerMethod", method=RequestMethod.POST)
public String doSomething() {
System.out.println( wantToSeeListOfPeople );
}
Don't worry about the response for this question, all I care about is handling the request, I know how to deal with the responses.
EDIT:
I've got more sample code, but I can't get it to work, what am I missing here?
select javascript:
var person = new Object();
person.name = "john smith";
person.age = 27;
var jsonPerson = JSON.stringify(person);
$.ajax({
url: "test/serialize",
type : "POST",
processData: false,
contentType : 'application/json',
data: jsonPerson,
success: function(data) {
alert('success with data : ' + data);
},
error : function(data) {
alert('an error occurred : ' + data);
}
});
controller method:
public static class Person {
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
String name;
Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#RequestMapping(value = "/serialize")
#ResponseBody
public String doSerialize(#RequestBody Person body) {
System.out.println("body : " + body);
return body.toString();
}
this renders the following exception:
org.springframework.web.HttpMediaTypeNotSupportedException:
Content type 'application/json' not
supported
If the doSerialize() method takes a String as opposed to a Person, the request is successful, but the String is empty
Your jQuery ajax call produces the following application/x-www-form-urlencoded request body (in %-decoded form):
people[0][name]=dave&people[0][age]=15&people[1][name]=pete&people[1][age]=12&people[2][name]=steve&people[2][age]=24
Spring MVC can bind properties indexed with numbers to Lists and properties indexed with strings to Maps. You need the custom object here because #RequestParam doesn't support complex types. So, you have:
public class People {
private List<HashMap<String, String>> people;
... getters, setters ...
}
#RequestMapping("/controllerMethod", method=RequestMethod.POST)
public String doSomething(People people) {
...
}
You can also serialize data into JSON before sending them and then use a #RequestBody, as Bozho suggests. You may find an example of this approach in the mvc-showcase sample.
if you have <mvc:annotation-driven> enabled then:
#RequestMapping("/controllerMethod", method=RequestMethod.POST)
public String doSomething(#RequestBody List<Person> people) {
System.out.println( wantToSeeListOfPeople );
}
(List<Person> might not be the structure you would like to obtain, it's just an example here)
You can try setting the Content-Type of $.ajax to be application/json, if it doesn't work immediately.
Have a look at Jackson's spring integration. It is incredible easy to use and powerful.
A question/answer on SO that can help guide on this:
Spring 3.0 making JSON response using jackson message converter

Resources