Null parameter in OData Custom Action - c#

I want a custom action added in my OData controller for adding an entity. I can do that in standard Post method supplied in OData controller but I have some custom code that get's overwritten when I refresh the controller from database if I add an association.
Here is the custom method I've added
[HttpPost]
public IHttpActionResult CreateValidCombination(ValidCombination validCombination)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Ok();
}
The request header is same as the standard post method
POST http://localhost:20152/Admin/odata/ValidCombinations/fn.CreateValidCombination HTTP/1.1
Accept: */*
Content-Type: application/json
Referer: http://localhost:20152/Admin/index.html
Accept-Language: en-CA,en;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Connection: Keep-Alive
Content-Length: 284
DNT: 1
Host: localhost:20152
Pragma: no-cache
The Action is configured in WebApiConfig.cs as
builder.EntityType<ValidCombination>().Collection
.Action("CreateValidCombination")
.Returns<IHttpActionResult>();
The problem is while the standard OData Contoller Post method receives the parameter of type "ValidCombination" proper, customer method receives it as null.
I've checked request headers and body in fiddler and its same in both the cases. I've even tried putting [FromBody] in front of the parameter but to no avail. I am trying to get value into the parameter for my custom method.
Has anyone faced this situation before. Is there a way I can debug why the parameter is not properly being deserialized even though request and body are same for both the methods?

According to http://odata.github.io/WebApi/#04-07-action-parameter-support, in your customized action method, parameter type should be ODataActionParameters instead of ValidCombination, OData WebApi's code is in github, is very easy to debug, you can find lots of function tests too.

Related

Unsupported Media Type when consuming text/plain

I receive the following response when trying to consume text/plain:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
"title": "Unsupported Media Type",
"status": 415,
"traceId": "|b28d0526-4ca38d2ff7036964."
}
Controller definition:
[HttpPost]
[Consumes("text/plain")]
public async Task<IActionResult> PostTrace([FromBody]string body)
{ ... }
HTTP message:
POST /api/traces HTTP/1.1
Content-Type: text/plain
User-Agent: PostmanRuntime/7.19.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 37d27eb6-92a0-4a6a-8b39-adf2c93955ee
Host: 0.0.0.0:6677
Accept-Encoding: gzip, deflate
Content-Length: 3
Connection: keep-alive
I am able to consume JSON or XML just fine. What am I missing?
Reference: Accepting Raw Request Body Content in ASP.NET Core API Controllers:
Unfortunately ASP.NET Core doesn't let you just capture 'raw' data in any meaningful way just by way of method parameters. One way or another you need to do some custom processing of the Request.Body to get the raw data out and then deserialize it.
You can capture the raw Request.Body and read the raw buffer out of that which is pretty straight forward.
The easiest and least intrusive, but not so obvious way to do this is to have a method that accepts POST or PUT data without parameters and then read the raw data from Request.Body:
[HttpPost]
[Route("api/BodyTypes/ReadStringDataManual")]
public async Task<string> ReadStringDataManual()
{
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
return await reader.ReadToEndAsync();
}
}
Request:
POST http://localhost:5000/api/BodyTypes/ReadStringDataManual HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/plain
Host: localhost:5000
Content-Length: 37
Expect: 100-continue
Connection: Keep-Alive
Windy Rivers with Waves are the best!

Web api behaves "stateful" in some successive calls from different client issue

I have a web api consumed by mobile apps. I can reproduce case with postman also with appropriate params.
Here are my postman call captured by fiddler:
GET http://localhost/WebApi/api/User/GetAnnouncement?id=22 HTTP/1.1
Content-Type: application/json
ApiKey: someKey
AuthenticationToken: someGuid1
UserId: 6524
DeviceId: someGuid2
LocalDate: 538294155.662561
OsTypeId: 1
LoginToken: someGuid3
CompanyId: 2
cache-control: no-cache
Postman-Token: b863afdd-b04c-4a4d-b473-69d5ecef622e
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: localhost
cookie: ASP.NET_SessionId=nk4g3zzfyi0n3xomfw5dxxxx
accept-encoding: gzip, deflate
Connection: keep-alive
and my issue occurs in authorize action filter:
public class BasicAuthorizeAttribute : FilterAttribute
{
}
public class BasicAuthorizeFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
//!!!HERE when I debug, in watch I can see already authHeader has value "in some calls"
System.Threading.Thread.SetData( System.Threading.Thread.GetNamedDataSlot("authHeader"), "someValueComingFromRequestHeader" );
}
}
At the very beginning of the OnAuthorization (see !!!HERE line in the code), I can see in watch this expression:
System.Threading.Thread.GetData(System.Threading.Thread.GetNamedDataSlot("authHeader"))
has the value even though I expect it is always null. It has even the value from previous client.
Actually issue come to as a bug "session" mingled (I mean mixed).
This pieces code is on the my company's framework so something is weirdly wrong.
I can give as much as I can so far. Please ask any info necessary.
What could be the cause?
I am daring to ask this because it is possible the issue obvious may be.
Note: I have the same case while none debugging with two phones connected to my pc via proxy settings.

Model Binder not working on first request after App Pool recycle

I have a problem with the model binder. Each time after I recycle the App Pool, the first request to the server fails to perform model binding. Request.InputStream has the proper request parameters, but the Action parameters are null and Request.Params does not contain them either.
This appears to happen on the first POST request with JSON. The real first request is a GET to Index(), which returns the view. After that, the client makes an AJAX request that fails.
This is what my Action looks like:
public async Task<ActionResult> GetQueryCounters(string query) { ... }
This is the contents of Request.InputStream on the first request (it's the same on subsequent requests):
{"query":"SELECT count(*) FROM accounts"}
This is the raw request as seen in Fiddler:
POST https://[url]/GetQueryCounters HTTP/1.1
Host: [url]
Connection: keep-alive
Content-Length: 281
Origin: https://[url]
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36
Content-Type: application/json; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
x-ajaxRequest: true
X-Requested-With: XMLHttpRequest
__RequestVerificationToken: [snip]
Referer: https://[url]
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,he;q=0.6
Cookie: [snip]
{"query":"SELECT count(*) FROM accounts"}
What could be causing this?

Dynamic model binding with ASP.NET WEB API

According to Scott Hanselman, on his blog, I should be able to do dynamic model binding and return a dynamic.
I have a Web API controller that contains a single method:
public dynamic Post(dynamic data)
{
return data;
}
When I make the following call from Fiddler, I am getting a null returned.
POST http://localhost:57856/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:57856
Content-Type: "application/json"
Content-Length: 22
{"Name": "jlucpicard"}
What am I missing here? Shouldn't it return JSON for data? This is a simpler follow-up to my original question ASP.NET WEB API not binding to dynamic object on POST.
Your action is returning null because your "data" parameter is not being bound to the incoming json data.
Remove the quotes from "application/json" in your Content-Type header to bind to the data.
Content-Type: application/json

Does it matter that a servicestack.net OPTIONS request returns a 404?

I'm using the method described at https://github.com/ServiceStack/ServiceStack/wiki/New-Api to enable CORS. This seems to work, i.e. I get the correct Access-Control headers back in the response, but the status code is a 404. Does this matter?
For completeness I'd prefer to return a non-error code. Reading http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html it seems as though I ought to be returning a 200. What's the best way to do this?
The service method definition is:-
[EnableCors]
public void Options(ApiKeyRequest apiKeyRequest)
{
}
The HTTP request is:-
OPTIONS http://myreallycoolwebapi/apikeyrequests HTTP/1.1
Host: myreallycoolwebapi
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
Access-Control-Request-Headers: origin, content-type, accept
Accept: */*
Referer: http://localhost:8000/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
and the response is:-
HTTP/1.1 404 Not Found
Server: nginx
Date: Sat, 17 Nov 2012 18:06:01 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 3
Connection: keep-alive
Cache-Control: private
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
404
UPDATE 1
This is my resource type.
[Route("/apikeyrequests", "POST")]
public class ApiKeyRequest : IReturn<ApiKeyRequest>
{
public string EmailAddress { get; set; }
}
The POST requests work just fine.
I still haven't seen anything matching the expected route /apikeyrequests.
Do you have a custom route definition, e.g:
[Route("/apikeyrequests")]
public class ApiKeyRequest {}
Added for the request?
The CORS spec itself explicitly indicates that a non-200 preflight response is an error. See the "If the response has an HTTP status code that is not 200" section here:
http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
I don't know what the motivation is for this requirement. Perhaps its because a 404 could introduce confusion as to whether the endpoint actually exists.

Categories