HTTPException 406 from 1 REST/OData Controller but not others - c#

I have several Odata(4.0)/Rest controllers in my project(c#/.net 4.62). All but 1 work as expected. This is the controller that returns a 406 every time I call it.
[RoutePrefix("api/SecurityConfig")]
[ApiExplorerSettings(IgnoreApi = false)]
public class SecurityConfigController : ODataController
{
[Route("GetRoleGroupWithRoles")]
[EnableQuery(MaxExpansionDepth = 6)]
[HttpGet]
public virtual async Task<IHttpActionResult> GetRoleGroupWithRoles([FromODataUri] Guid key)
{
return Ok("Hello World");
}
}
I call using the following headers:
Accept: application/* (though I've tried just "*" and "application/json")
Content-Type: application/json
I'm baffled why this controller is not working and the rest of them are......thanks in advance for any thoughts on how I can figure this out.

I think you are missing the first two steps of building an OData service. ODataController, as the name says, only works with OData routes. You need to build an EDM model representing your OData service, and, add an OData route exposing that EDM model. Refer to this official documentation and blog post for details on how to build OData services. [source]:How can I avoid a 406 when receiving an OData.PageResult<T>?

Related

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints

I have an ASP.Net Core WebAPI, I got below requirement as
I have 2 methods to handle HTTP GET requests, the first one for GetCustomer by id(string) and the other one to GetCustomer by email(string).
//GET : api/customers/2913a1ad-d990-412a-8e30-dbe464c2a85e
[HttpGet("{id}")]
public async Task<ActionResult<Customer>> GetCustomer([FromRoute]string id)
{
}
// GET: api/customers/myemail#gmail.com
[HttpGet("{name}")]
public async Task<ActionResult<Customer>> GetCustomerByEmail([FromRoute]string email)
{
}
when I try to this Endpoint, I get exception as:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
Which is quite Obivious & understood.
It could be easily addressed by appending/prepending some string in the route like
/api/customer/{id}
/api/customer/emai/{id}
However not so convinced with this apporach however on googling I get the below SO link
How to handle multiple endpoints in ASP.Net Core 3 Web API properly
But this guy has one parameter as int while the other as string so route constraint rescued.
however, I see some people posting/suggesting on the same post to add [Route("")] but I didn't get how this attribute is useful ?
afaik, Route("") and HTTPGet("") //any HttpVerb serves the same purpose?
Anyways how could I handle my requirement elegantly?
You can add route constraints to disambiguate between the two routes:
[HttpGet("{id:guid}")]
[HttpGet("{name}")]
You could also create your own email constraint or use regex(.) constraint for the email parameter.

Asp.net Core 2 API POST Objects are NULL?

I have a .net Core 2 API setup with some test function. (Visual Studio 2017)
Using postman I do a post with the raw data to that method, but the model is just blank? Why?
// POST api/Product/test
[HttpPost]
[Route("test")]
public object test(MyTestModel model)
{
try
{
var a = model.SomeTestParam;
return Ok("Yey");
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
public class MyTestModel
{
public int SomeTestParam { get; set; }
}
You need to include the [FromBody] attribute on the model:
[FromBody] MyTestModel model
See Andrew Lock's post for more information:
In order to bind the JSON correctly in ASP.NET Core, you must modify your action to include the attribute [FromBody] on the parameter. This tells the framework to use the content-type header of the request to decide which of the configured IInputFormatters to use for model binding.
As noted by #anserk in the comments, this also requires the Content-Type header to be set to application/json.
To add more information to the accepted answer:
There are three sources from which parameters are bound automatically without the use of an Attribute:
Form values: These are form values that go in the HTTP request using
the POST method. (including jQuery POST requests).
Route values: The set of route values provided by Routing
Query strings: The query string part of the URI.
Note that Body is NOT one of them (though I think it should be).
So if you have values that need to be bound from the body, you MUST use the attribute binding attribute.
This tripped me up yesterday as I assumed that parameters from the Body would be bound automatically.
The second minor point is that only one parameter can be bound to the Body.
There can be at most one parameter per action decorated with [FromBody]. The ASP.NET Core MVC run-time delegates the responsibility of reading the request stream to the formatter. Once the request stream is read for a parameter, it's generally not possible to read the request stream again for binding other [FromBody] parameters.
Thus if there is more than one parameter you need, you need to create a Model class to bind them:
public class InputModel{
public string FirstName{get;set;}
public string LastName{get;set;}
}
[HttpPost]
public IActionResult test([FromBody]InputModel model)...
The Docs
Alternatively, add the [ApiController] attribute to your controller.
This has annoyingly affected me so many times (months apart) that I want this answer visible.
I deal with this issue for some hours. This problem stems from several reasons. Let's consider the request is Reactjs (javascript) and backend (API) is Asp .Net Core.
in the request, you must set in header Content-Type:
Axios({
method: 'post',
headers: { 'Content-Type': 'application/json'},
url: 'https://localhost:44346/Order/Order/GiveOrder',
data: order,
}).then(function (response) {
console.log(response);
});
and in the backend (Asp .net core API) u must have some setting:
1. in Startup --> ConfigureServices:
#region Allow-Orgin
services.AddCors(c =>
{
c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
});
#endregion
2. in Startup --> Configure before app.UseMvc() :
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
3. in the controller before action:
[EnableCors("AllowOrigin")]
In my case I had { get; set; } missing in my .cs model which results in an object with all members null on POST.

Route Disambiguation in ASP.NET Core (MVC 6)

When ASP.NET Core encounters ambiguously named routes, it becomes inert. That is, the application will run without exceptions thrown but, it will fail to process any requests, on any controllers. The calling client receives 500 responses.
I'll show how I got into this mess, and I'd like suggestions of how to fix it.
I have a controller that looks like this:
[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
public class AddressesController : Controller
{
[HttpGet("{aid}", Name = "PostalLink")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
[HttpPost]
[SwaggerResponseRemoveDefaults]
[SwaggerResponse(HttpStatusCode.Created, Type = typeof(PostalRecord))]
public async Task<IActionResult> CreateAddress(Guid id, [FromBody] PostalAddress address)
{
address.ID = Guid.NewGuid();
await createAddress.Handle(address);
return CreatedAtRoute("PostalLink", new { id = id, aid = address.ID });
}
Why the two route prefixes on the controller? Because it fits my microservices (and Swagger documentation) strategy. Nevertheless, in this example ASP.NET Core does not know how to resolve the route name "PostalLink" because it is implicitly bound to the two prefixes:
[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
I can fix the problem simply by changing the HttpGet so that instead of this:
[HttpGet("{aid}", Name = "PostalLink")]
I have this:
[HttpGet("{aid}")] //the route is no longer "named"
Unfortunately, removing the route name is not a real option for me.
What is the prescribed way to fix this?
Below are some of the options I'm considering.
Possibility #1
Theoretically, ASP.NET could simply "figure it out" by itself. For example, if the current request resolved to the route containing the word "Subscribers", then the "PostalLink" name should reference that route. Seen this way, perhaps my code is exposing a bug, defect, or oversight in ASP.NET Core.
Possibility #2
I could collapse my two prefix routes into a single route like this:
[Route("api/{parent}/{id}/[controller]")]
This works, but it undermines my REST documentation strategy. I'm using Swashbuckle to publish endpoint metadata. I want a user of my API to expressly see that my "Addresses" API is serving either "Subscribers" or "Organizations". When I have two explicit route prefixes, the Swagger documentation works correctly (and I properly validate the URI used by the client).
Possibility #3
I could simply override the two prefixes like this:
[HttpGet("~/api/Subscribers/{id}/Addresses/{aid}", Name = "SubscriberLink")]
[HttpGet("~/api/Organizations/{id}/Addresses/{aid}", Name = "OrganizationLink")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
Now my documentation and route validation works, but my implementation is forced to check which route was used to reach the endpoint. That is very doable, but very annoying.
Possibility #4
Perhaps there is a more expressive way to handle this problem without attribute-based-routing? If yes, please share!
Details
My project.json is configured as follows:
"frameworks": {
"dnx46": { }
},
I am using DNX SDK version 1.0.0-rc1-update1. Also, I posted a related SO question for those who would like more context of what I am trying to do.
If your route names are the same for all your actions, why not specify them directly on the controller ?
[Route("api/Subscribers/{id}/[controller]", Name = "SubscriberLink")]
[Route("api/Organizations/{id}/[controller]", Name = "OrganizationLink")]
public class AddressesController : Controller
{
[HttpGet("{aid}")]
public async Task<IActionResult> GetAddress(Guid id, Guid aid)
{
//...implementation is irrelevant for this question.
}
}
Have you looked into attribute routing?
E.g. Registering routes with ASP.Net 5's MVC 6 Attribute Routing
Sample from the relevant documentation:
In the following example, app.UseMvc(); is used in the Configure method and no route is passed.
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
[Route("Home/About")]
public IActionResult About()
{
return View();
}
[Route("Home/Contact")]
public IActionResult Contact()
{
return View();
}
}
The HomeController.Index() action will be executed for any of the URL paths /, /Home, or /Home/Index.

Metadata document shows a POST based sample for a GET based DTO

I have the following DTO where the URI should be like api/logs?verbose=, where verbose can be true or false.
[Route("/api/logs", "GET")]
public class GetLogs
{
public bool Verbose { get; set; }
}
My service is:
public object Get(GetLogs getLogs)
{
//For brevity
return getLogs;
}
I see that the sample that generated in metadata document mentions POST instead of GET, which I explicitly set.
POST /json/oneway/GetLogs HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: length
{"Verbose":false}
How can I fix this?
To my knowledge, that is just a sample. Don't take the "POST /json/oneway/GetLogs HTTP/1.1" literally. You should be using the actual Route endpoints shown earlier in the same page, which would likely be:
The following routes are available for this service:
GET /api/logs
In my ServiceStack projects, they are all the same way - the sample is POST, even if the available routes are only: GET, OPTIONS
I did some searching, and this appears to be hardcoded in the class: ServiceStack.Metadata.OperationControl
So you could accept you "can't do it" with the current release. Or figure out how to override or patch OperationControl template rendering.

Web Api not binding to model

I'm trying to post a JSON object to a Web Api URL and it's not binding to the model.
This seems to be the same problem: ASP.Net Web Api not binding model on POST
I tried everything that they did and it still doesn't work. The one difference you may notice is that I'm not using the DataContract attributes, but I don't believe they should be required, and didn't make any difference when I tried them.
public class MyModel
{
public int Id { get; set; }
}
Public class MyController : ApiController
{
public int Save(MyModel myModel)
{
// myModel is always null
return 0;
}
}
You appear to be missing [HttpPost] attribute from your controller method. It appears in the above case this is actually not strictly required, perhaps this is only needed when posting primitives?
Also just as a note I would use a more REST based syntax if you are using WebApi for example use methods Get, Post, Put ect on your controller rather than named methods
EDIT:
You also have one other really subtle issue with your post. A header line cant end with a ; so Content-Type: application/json; charset=utf-8; should be Content-Type: application/json; charset=utf-8
It could be an encoding issue.
I changed my encoding and model binding is done succesfully.
client.Encoding = Encoding.UTF8;

Categories

Resources