POST action taking JSON and returning JSON with ASP.NET - asp.net

I'm developing a web service in which I have resources of type A owning resources of type B.
I want to develop an API for getting stats about an A, but only considering a subset of its Bs.
So I'd have a route taking an A's ID, a collection of IDs of Bs, and returns the stats. I'd use it like the following:
POST /api/StatsAboutA/{aId}
JSON payload:
[1, 4, 12]
And it'd return something like that:
[
{"key": ..., "value": ...},
...
]
Here is my controller:
class StatsAboutAController : ApiController
{
// ...
// POST api/StatsAboutA/{aId}
[HttpPost]
public Stats Post(long aId, IEnumerable<long> bIds)
{
A a = _aRepository.SelectById(aId);
if (a == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return _aRepository.CollectStats(a, bIds);
}
// ...
}
I can't manage to configure my router so that my controller matches my route.
Here is the error message I get:
{
"message": "No HTTP resource was found that matches the request URI 'http://localhost:51398/api/StatsAboutA/1'.",
"messageDetail": "No action was found on the controller 'StatsAboutA' that matches the request."
}

Change the method signature to
public Stats Post(long aId, [FromBody]IEnumerable<long> bIds)
Because it is trying to get the bIds from the URI and that route is not defined.

Related

Create custom response in .NET Core 5 with OData 8 controller

I'm using OData 8 in .NET Core 5 with the [EnableQuery] attribute. Problem is when I want to return Problem() or BadRequest("Some message"), the API always returns some default OData BadRequest and never the message I wanted (only when [EnableQuery] attribute is there).
Example:
[HttpGet]
[EnableQuery(EnsureStableOrdering = false )]
public IActionResult GetList(ODataQueryOptions<List> queryOptions)
{
if (queryOptions.Filter == null)
{
return BadRequest( "Filter is required for this endpoind!" );
}
try
{
queryOptions.Filter.Validator = new OdataCustomFilterValidator();
this.BaseValidateQueryOptions(queryOptions);
}
catch (ODataException ex)
{
return this.BadRequest(ex.Message);
}
IQueryable<List> list = this._service.GetList();
return this.Ok(list);
}
So in the above example, if the code gets to the first IF, i do not recieve this message but ALWAYS the same Odata error:
{
"error": {
"code": "",
"message": "The query specified in the URI is not valid. The requested resource is not a collection. Query options $filter, $orderby, $count, $skip, and $top can be applied only on collections.",
"details": [],
Hi you ran into this bug which was fixed in OData/WebApi https://github.com/OData/WebApi/issues/2511 but it seems like it has not yet been migrated to AspNetCoreOData, A pull request exists which when merged and published should allow you to continue with your use case which is valid.

ASP.NET Web API IIS hosting REST Methods produces NULL in RethinkDB

I'm making a Web API for handle my Android app's HTTP requests and rethinkDB connection. But something wrong in IIS i think. It produces null values for my User identity when i send POST request to the API. My db is running on localhost.
My POST method is (there is no DB syntax errors.) =
public IHttpActionResult PostNewUserModel(StudentViewModel studentViewModel)
{
if (!ModelState.IsValid)
return BadRequest("Invalid data.");
var conn = connection();
var newStudentViewModel = new StudentViewModel
{
Id = studentViewModel.Id,
FirstName = studentViewModel.FirstName,
LastName = studentViewModel.LastName
};
R.Db(MYDBNAME).Table(MYTABLENAME).Insert(newStudentViewModel).Run(conn);
return Ok();
}
I POST'ed this entity =
{
"Id": 6,
"FirstName": "Nihat Can Doğamaz",
"LastName": "CANITIN"
}
But i see this entity in RethinkDB table like this =
{
"FirstName": null ,
"Id": 0 ,
"LastName": null
}
How to solve it ?
I solve the error. DB cannot recognize returned JSON format. It must be identified in POSTMAN. You must select JSON(application/json) Body format to write your JSON objects into your db.

use json format in an URL

I am building a rest API with asp.net my problem is that when I try to add a student to my database like that :
http://localhost:50001/api/Students?&FirstName=cc&LastName=cc&Email=student10#gmail.com&DropOut=false&Live=false&ClassId=1&ImageId=1
I get "the value variable is null",
this is my code to add a student:
// Get All Students
[Route("api/Students")]
public IEnumerable<Student> Get()
{
return _StudentService.Queryable().ToList();
}
// Insert Student
[Route("api/Students/")]
public IEnumerable<Student> Post(Student value)
{
cc.Students.Add(value);
cc.SaveChanges();
return Get();
}
I have used "Fiddler web Debugger" to test my URLs an it works only in this way:
now If I have an angularJS client that tries to add a new student to the database,how can I send data as a json format in an URL
this is how I add a new student from my client angularJS:
$http({method: 'POST', url: 'http://localhost:50001/api/Students?&FirstName=cc&LastName=cc&Email=student10#gmail.com&DropOut=false&Live=false&ClassId=1&ImageId=1})
.success(function (data) {
console.log("success");
}).error(function (data, status, headers, config) {
console.log("data error ...");
});
thanks a lot for help
If you are saying you want a true Rest API you should continue to use the POST verb as it is more semantically right for creating a new student.
Passing a new student on the URL is possible but not in the configuration you have provided.
Your API method expects a POST request and that the new student be located in the HTTP body.
Just configure your angular call to use jsonData and post it to your API.

Get matched route name in Web API

In my Web API handler I need to get the name of the route that matched the request.
public class CurrentRequestMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var route = request.GetRouteData().Route;
//now what?
return base.SendAsync(request, cancellationToken);
}
}
Currently there is no way to retrieve the route name of a route in Web API. You can take a look at the HttpRouteCollection source code here for more details. If route name is definitely required for your scenario, you could stick in the route name in the data tokens of a route. (note that currently attribute routing doesn't provide a way to access the data tokens)
Update - 6/23/2014
With latest improvements(5.2 RC) in the area of attribute routing, you can do something like following to insert route names into data tokens.
config.MapHttpAttributeRoutes(new CustomDefaultDirectRouteProvider());
public class CustomDefaultDirectRouteProvider : DefaultDirectRouteProvider
{
public override IReadOnlyList<RouteEntry> GetDirectRoutes(HttpControllerDescriptor controllerDescriptor,
IReadOnlyList<HttpActionDescriptor> actionDescriptors, IInlineConstraintResolver constraintResolver)
{
IReadOnlyList<RouteEntry> coll = base.GetDirectRoutes(controllerDescriptor, actionDescriptors, constraintResolver);
foreach(RouteEntry routeEntry in coll)
{
if (!string.IsNullOrEmpty(routeEntry.Name))
{
routeEntry.Route.DataTokens["Route_Name"] = routeEntry.Name;
}
}
return coll;
}
}
Access it like this:
reequest.GetRouteData().Route.DataTokens["Route_Name"]
It's perhaps a bit late to answer this, but I found myself in the same situation (that is I need to generate an URL while not having the corresponding IHttpRoute name). You can however generate an URL with just the Route and the HttpRequestMessage.
var parameters = new Dictionary{{"id" , 123}, {HttpRoute.HttpRouteKey, true}};
var path = Route.GetVirtualPath(request, parameters);
var uri = path.VirtualPath;
The important part is to add HttpRoute.HttpRouteKey to the parameters, if this value is not used GetVirtualPath returns null.
see code in HttpRoute.cs
// Only perform URL generation if the "httproute" key was specified. This allows these
// routes to be ignored when a regular MVC app tries to generate URLs. Without this special
// key an HTTP route used for Web API would normally take over almost all the routes in a
// typical app.
if (values != null && !values.Keys.Contains(HttpRouteKey, StringComparer.OrdinalIgnoreCase))
{
return null;
}

ASP.NET Web API Controller Function Executes when entering not mapped action route

I have a controller function which accepts a strongly typed model as parameter.
When i enter ANY url mapping to the controller but not on a specific action on the post request ,
then the controller executes this function instead of returning a 404 code.
When i Change the function's parameter to a primitive type variable then the problem does not occur. (i have tried using other strongly typed models as parameters and again the problem occurs)
Here's the function.
public class PhoneApiController : ApiController
{
[HttpPost]
public HttpResponseMessage RegisterApp(RegisterAppInfo appInfo)
{
var resp = Request.CreateResponse(HttpStatusCode.OK, new
{
Success = true,
AppId = 1000,
IdAlias = "QAUBC9",
appInfo = appInfo
});
return resp;
}
}
So when i enter for example
localhost:51464/Api/PhoneApi/Sajsdkasjdklajsasd
the function executes normally.!
I am using the default Route config
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I don't know if this is a bug or i am doing something wrong.
The URI /Api/PhoneApi/Sajsdkasjdklajsasd does match your route template api/{controller}/{id} with {controller} matching PhoneApi and {id} matching Sajsdkasjdklajsasd. I assume you are making a POST to this URI. So, Web API is mapping your request to the action method RegisterApp with [HttpPost] in the controller class PhoneApiController.
As far as the junk stuff in the URI, it gets mapped to {id}. But your parameter is RegisterAppInfo, which is a complex type and that gets bound from request body and not the URI. That's why it works when you have the complex type. The simple types are bound from URI, query string.
If you have the action method as public HttpResponseMessage RegisterApp(string id, Abc appInfo), you will see that this id parameter gets populated with "Sajsdkasjdklajsasd".
For MVC 4.5 this is the only thing that works
There is currently a bug about this.
Below is a work around in order to get the following route types work
api/{controller}/ //Get All
api/{controller}/{Id} //Get for id
api/{controller}/{Id}/{Action}/ //Get all for action for controller with Id
you need to do the following.
Change your routing over to. (Note the default action..)
config.Routes.MapHttpRoute(
name : "DefaultAPi",
routeTemplate : "api/{controller}/{id}/{action}",
defaults: new
{
id = RouteParameter.Optional,
action = "DefaultAction"
}
);
In your controller change the base methods over to
[ActionName("DefaultAction")]
public string Get()
{
}
[ActionName("DefaultAction")]
public string Get(int id)
{
}
[ActionName("SpaceTypes")]
public string GetSpaceTypes(int id)
{
}
Now everything should work as expected..
Thanks to Kip Streithorst full this, for a full explanation
The way routing works in Web API is:
First it matches the URI against route template. At this stage, it's not looking at your controller actions
Then it looks for a matching controller
Then it looks for a method where (a) the action matches (POST in this case) and (b) every simple parameter type is matched with a value from the URI.
If there is a complex parameter type, it tries to read that from the request body.
By default, Web API tries to bind "simple" parameter types (like int) from the URI, and tries to read complex types from the request body.
See here for details: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection

Resources