I need to pass a dynamic JSON object to my Web API controller so that I can process it depending on what type it is. I have tried using the JSON.NET example that can be seen here but when I use Fiddler, I can see that the passed in JObect is always null.
This is an exert from the example pasted into Fiddler:
POST http://localhost:9185/api/Auto/PostSavePage/ HTTP/1.1
User-Agent: Fiddler
Content-type: application/json
Host: localhost
Content-Length: 88
{AlbumName: "Dirty Deeds",Songs:[ { SongName: "Problem Child"},{ SongName:
"Squealer"}]}
Ans here is my very simple Web API controller method:
[HttpPost]
public JObject PostSavePage(JObject jObject)
{
dynamic testObject = jObject;
// other stuff here
}
I am new to this and I have a couple of questions around this area:
Am I doing something wrong in this particular example?
Arguably, more importantly, is there a better way to pass in a dynamic JSON object (from an JavaScript AJAX post)?
As per Perception's comment your JSON doesn't look valid. Run it through JSONLint and you get:
Parse error on line 1:
{ AlbumName: "Dirty De
-----^
Expecting 'STRING', '}'
Change it to have " around the field names:
{
"AlbumName": "Dirty Deeds",
"Songs": [
{
"SongName": "Problem Child"
},
{
"SongName": "Squealer"
}
]
}
Also have you tried swapping out your JObject for either a JToken or a Dynamic object (e.g. here)?
[HttpPost]
public JObject PostSavePage(JToken testObject)
{
// other stuff here
}
OR
[HttpPost]
public JObject PostSavePage(dynamic testObject)
{
// other stuff here
}
Thanks to everyone who helped here. Unfortunately, I never got to the bottom of what was wrong.
I ported the project over piece by piece to a new project and it works fine.
For info, I have a RouteConfig class, which is quite simple at the moment:
public class RouteConfig
{
private static string ControllerAction = "ApiControllerAction";
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: ControllerAction,
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
My call into the API now uses JSON.Stringify:
$.ajax("http://localhost:54997/api/values/PostSavePage/", {
data: JSON.stringify(jObject),
contentType: 'application/json',
type: 'POST'
});
The original API action works.
Note, that I'm only playing with this at the moment so the code is not the best but I thought it may be useful in basic form in case someone else has a similar issue.
Related
I generated swagger for micronaut using the instructions provided in https://micronaut-projects.github.io/micronaut-openapi/latest/guide/index.html
So I have a controller method like:
#Consumes("application/vnd.api+json")
#Produces("application/vnd.api+json")
#Post("/{id}/users")
#RequestBody
public HttpResponse addAndAssignTarget(#PathVariable("id") Long projectId, #Body #Parameter() JsonNode user) {
I am not using a POJO for adding users for another reason which is out of context for this question. Thus, the generated swagger ui shows {} as example for request body. I would like to change this to something like. How can I do this?
{
"data" : {
"type": "projects",
"attributes": {
"name": "some-name1",
"description": "some-description",
"partner_company": "some-compnay"
}
}
}
It looks much better if you use POJO. However, you can add an example as string, but the problem with that if you have any nested objects the quotation marks won't look nice (will be escaped "):
#Consumes("application/vnd.api+json")
#Produces("application/vnd.api+json")
#Post("/{id}/users")
#RequestBody
public HttpResponse addAndAssignTarget(#PathVariable("id") Long projectId,
#Body #RequestBody(content = #Content(schema = #Schema(example = "[0, 2, 3]"))) JsonNode user){
}
Also it is not a Paramater, but a #Body and #RequestBody for annotation purposes.
Output will be:
"[0, 2, 3]"
**AngularJs Post Request :**
var url = 'http://localhost:54047/api/JobSaleBid';
var student = {
"firstname": 'dfdsfsdfsdfsdfsdfsdf',
"lastname": 'k.waqas26#gmail.com'
};
data = $http.post(
url,
JSON.stringify(student),
{
headers: {
'Content-Type': 'application/json'
}
}
).success(function (data) {
$scope.person = data;
});
**Model:**
public class student
{
public string firstname {get;set;}
public string lastname {get;set;}
}
**Controller:**
public class JobSaleBidController : BaseApiController
{
public string Post([FromBody]student model)
{
return JsonConvert.SerializeObject(model);
}
}
**Route Config**
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
While Posting I am getting the following errors
Failed to load resource: the server responded with a status of 405 (Method Not Allowed)
index.html:1 XMLHttpRequest cannot load http://localhost:54047/api/JobSaleBid. Invalid HTTP status code 405
Request Detail:
Remote Address:127.0.0.1:54047
Request URL:http://localhost:54047/api/JobSaleBid
Request Method:OPTIONS
Status Code:405 Method Not Allowed
The requested resource does not support http method 'GET'.
Can any one tell me what is wrong, I have tried so many solutions but still not getting it worked, you help will be appreciated.
The jquery post should look like this
$http({
method: 'POST',
url: url,
data: JSON.stringify(student),
headers: {'Content-Type': 'application/json'}
});
and would strongly recommend you to use a Service/Factory to get data and inject them in your controller and then use them.
Edit:
Try adding [HttpPost] over your action.
Have a look at this page: http://www.asp.net/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications
I've encountered the same problem before. For me, adding the ExtensionlessUrlHandler fixed the problem.
I have this method in a controller in a web api
public class UserController : ApiController
{
[HttpPost]
public HttpResponseMessage Login(string name, string password){
//dothings
}
}
but if I try a ajax request:
$.ajax({
type: "POST",
url: "http://localhost:19860/Api/User/Login",
data: { name: "name", password: "12345" },
success: succ,
error: err
});
It gives me the error:
Message: "No HTTP resource was found that matches the request URI 'http://localhost:19860/Api/User/Login'."
MessageDetail: "No action was found on the controller 'User' that matches the request."
but, if i remove the parameters it works!
public class UserController : ApiController
{
[HttpPost]
public HttpResponseMessage Login(){
string name= HttpContext.Current.Request.Params["name"];
string password= HttpContext.Current.Request.Params["password"];
// name and password have the value that i passed in the ajax call!!
}
}
why it is like this?
For reasons unrelated to this question, I can't change the web api, so I have to mantain the:
public HttpResponseMessage Login(string name, string password)
format.
Can I mantain this format and be able to make the ajax call?
You can not post multiple parameter to Web-API methods as mentioned in this blog post
There is few alternative to this as follow.
1. Try to pass it on queryString.
Change your ajax function call like below.
$.ajax({
type: "POST",
url: "http://localhost:19860/Api/User/Login?name=name&pass=pass",
data: { name: "name", password: "12345" },
success: succ,
error: err
});
you will get name = name and password = pass in your api controller.
2. Create your own binder as implemented in this blog post.
3. Wrap all the Web-API method parameter in one object or type (recommended).
From the above you can use 1st and 2nd approach as you mentioned in your question you can not change your api method signature.
I have a website that is in a pre-integration phase. In other words, I have ensured that the site runs fine on my local Development Server (VS2012) utilizing the dynamically generated ASP.NET Development Server that runs at the time of debug executions; and I have now created a sub-domain of my domain on the Web Host Server and deployed my site there.
My decision to do this was because I obviously don't want users accessing the site until it has undergone thorough testing on the actual Host. My problem is though most of the site functions without issue (including URL's), there are a few links that produce the HTTP 404 error.
"The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly."
It then references the View/Controller path in the context of the Error that I know exists.
Why it only happens for certain Uri requests and not others is somewhat puzzling to me.
So I am strongly suspecting that it has something to do with the Default Routing Configuration for MVC and I believe if I were to move the site to the main domain, the issue will likely resolve but then again, it would defeat the purpose of setting it up in the SubDomain for testing before public access.
I need some viable options here but don't no where to start.
Should I address the issue from the perspective of the Routing Configuration and create 2 separate Global.asax.cs files? One for the domain and the other for the subdomain testing? And if so, How should I modify the file to accomodate for the Subdomain.
Or is there a more elegant solution for approaching the Integration process?
----------------- UPDATE ---------------------
So I've been troubleshooting the problem and it appears as though the 404 Error is only being generated for a method in my Controller that is returning a string.
I have a function that is being called in my View that looks like this:
<script>
function Subscribe(slvl) {
$.ajax({
type: 'POST',
datatype: "text",
data: { level: slvl },
url: '#Url.Action("Upgrade", "Profile")',
success: function (result) {
if (result) {
window.location = result;
}
},
error: function onError(result) {
alert(result.responseText);
}
});
}
</script>
I cannot post the full details of the Controller but it simply returns a string and whether I were to post the entire method or a simple one, the results would be the same. So for illustration only it looks something like this.
[HttpPost]
public string Upgrade(string level)
{
var uri = "http://www.someUri.com?Upgrade=" + level;
return uri;
}
This code is producing a HTTP 404 Error complaining that the path Profile/Upgrade cannot be found.
But I've found that if I use reference to a different method being called in the same Controller with the only exception being that it returns an ActionResult to a different View, the Error goes away and I'm redirected to the alternate view.
Any Ideas? So maybe it has nothing to do with the Subdomain???
First of all you need to create your own Custom Route
public class SubDomainRoute : Route
{
private readonly string[] namespaces;
public SubDomainRoute(string url, object defaults, string[] namespaces)
: base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
{
this.namespaces = namespaces;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
if (the subdomain is the expected one, then update your route data)
{
var routeData = base.GetRouteData(httpContext);
if (this.namespaces != null && this.namespaces.Length > 0)
{
routeData.DataTokens["Namespaces"] = this.namespaces;
}
routeData.DataTokens["Area"] = "Your Subdomain Area";
routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString;
return routeData;
}
return null;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
if (is your expected subdomain)
{
return base.GetVirtualPath(requestContext, values);
}
return null;
}
}
Then you have to use the custom route in the area registration like this:
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.Add("YourAreaName_default", new CustomSubDomainRoute(
"{controller}/{action}/{id}",
new { area = "Your Area", controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { typeof(Controllers.HomeController).Namespace } // Namespaces defaults
));
}
And finally, my advise is to use redirect to route, because the routeData Area is lost between the different requests.
Use your global property route name when redirecting.
[Authorize]
public class YourController : Controller
{
protected virtual string RouteName
{
get
{
return "YourRouteName";
}
}
public ActionResult Test(params here)
{
return RedirectToRoute(this.RouteName, new { action = "your action" });
}
}
Ref: http://forums.asp.net/t/1967197.aspx?Subdomain+in+mvc4+can+not+redirect+to+controller+in+area+
I am writing a simple web api application. I came to a phase when I need to have two POST methods in my web api controller. One of these methods works and the other does not. My route table looks like this:
config.Routes.MapHttpRoute(
name: "ApiRouteWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Then I have my methods defined like this:
[HttpPost]
public bool PostTaskAgain(My3TasksWebAPI.Data.Task task)
{
var oldTask = _db.Task.Where(t => t.Id == task.Id).SingleOrDefault();
oldTask.DoAgain = true;
oldTask.DateUpdated = task.DateUpdated;
if (_db.SetOfTasks.Where(t => CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(t.DateCreated, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday) == CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday)).Any())
{
int currentSetOfTasksId = _db.SetOfTasks.OrderBy(s => s.DateCreated).FirstOrDefault().Id;
My3TasksWebAPI.Data.Task newTask = new Data.Task() { CreatedBy = oldTask.CreatedBy, DateCreated = oldTask.DateCreated, DateUpdated = null, DoAgain = false, Notes = string.Empty, SetOfTasksId = currentSetOfTasksId, Status = false, Title = oldTask.Title, UserId = oldTask.UserId };
_db.Task.Add(newTask);
}
_db.SaveChanges();
return true;
}
// Post api/values/PostSetOfTasks/{setOfTasks}
[HttpPost]
public bool PostSetOfTasks(My3TasksWebAPI.Data.SetOfTasks setOfTasks)
{
_db.SetOfTasks.Add(setOfTasks);
_db.SaveChanges();
return true;
}
When I try to call PostTaskAgain I get an internal server error. I think that it might be the routing table but I am not sure how to handle two post methods.
I call the web api from my asp.net mvc application like this:
HttpResponseMessage response = client.PostAsJsonAsync("api/values/PostSetOfTasks", model.SetOfTasks).Result;
and
HttpResponseMessage response = client.PostAsJsonAsync("api/values/PostTaskAgain", taskToPost).Result;
That means that I include the actions.
Working with POST in webapi can be tricky though conincidently, your issue turned out to be trivial. However, for those who may stumble upon this page:
I will focus specifically on POST as dealing with GET is trivial. I don't think many would be searching around for resolving an issue with GET with webapis. Anyways..
If your question is - In MVC Web Api, how to-
- Use custom action method names other than the generic HTTP verbs?
- Perform multiple posts?
- Post multiple simple types?
- Post complex types via jQuery?
Then the following solutions may help:
First, to use Custom Action Methods in Web API, add a web api route as:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}");
}
And they you may create action methods like:
[HttpPost]
public string TestMethod([FromBody]string value)
{
return "Hello from http post web api controller: " + value;
}
Now, fire the following jQuery from your browser console
$.ajax({
type: 'POST',
url: 'http://localhost:33649/api/TestApi/TestMethod',
data: {'':'hello'},
contentType: 'application/x-www-form-urlencoded',
dataType: 'json',
success: function(data){ console.log(data) }
});
Second, to perform multiple posts,
It is simple, create multiple action methods and decorate with the [HttpPost] attrib.
Use the [ActionName("MyAction")] to assign custom names, etc.
Will come to jQuery in the fourth point below
Third,
First of all, posting multiple SIMPLE types in a single action is not possible and there is a special format to post a single simple type (except for passing the parameter in the query string or REST style).
This was the point that had me banging my head with Rest Clients and hunting around the web for almost 5 hours and eventually, the following URL helped me. Will still quote the contents for the link may turn dead!
Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"na#Turbo.Tina"}
http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+Post+to+my+Web+Api
Anyway, let us get over that story. Moving on:
Fourth, posting complex types via jQuery, ofcourse, $.ajax() is going to promptly come in the role:
Let us say the action method accepts a Person object which had an id and a name. So, from javascript:
var person = { PersonId:1, Name:"James" }
$.ajax({
type: 'POST',
url: 'http://mydomain/api/TestApi/TestMethod',
data: JSON.stringify(person),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(data){ console.log(data) }
});
And the action will look like:
[HttpPost]
public string TestMethod(Person person)
{
return "Hello from http post web api controller: " + person.Name;
}
All of the above, worked for me!!
Cheers!
There was a problem with my LINQ query.
The response from the server was: {"$id":"1","Message":"An error has occurred.","ExceptionMessage":"LINQ to Entities does not recognize the method 'Int32 GetWeekOfYear(System.DateTime, System.Globalization.CalendarWeekRule, System.DayOfWeek)' method, and this method cannot be translated into a store expression.","ExceptionType":"System.NotSupportedException","StackTrace":" at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTransl‌​ator.Translate(ExpressionConverter parent, MethodCall....
After correcting the linq query everything is working fine. Visual studio was fine about me doing the linq query wrong.