c# - Web API action parameter is intermittently null -


related question: web api apicontroller put , post methods receive null parameters intermittently

background

while load testing existing web api project noticed lot of null reference exceptions result of parameter being null when posting action.

the cause seems custom message handler registered log requests while running in dev environments. removing handler resolves issue.

i understand in web api can read request body once , reading cause parameter null model binding wouldn't able take place. reason i'm using readasstringasync() method continuewith read body. looks behaving oddly in ~0.2% of requests (during local debugging using apache bench).

code

at basic level have following:

model

public class user {     public string name { get; set; } } 

api controller

public class userscontroller : apicontroller {     [httppost]     public void foo(user user)     {         if (user == null)         {             throw new nullreferenceexception();         }     } } 

message handler

public class testmessagehandler : delegatinghandler {     protected override task<httpresponsemessage> sendasync(httprequestmessage request, cancellationtoken cancellationtoken)     {         request.content.readasstringasync().continuewith((task) =>         {             /* stuff task.result */         });          return base.sendasync(request, cancellationtoken);     } } 

...which registered during app start

globalconfiguration.configuration.messagehandlers.add(new testmessagehandler()); 

i'm using webapi 4.0.30506.0, latest @ time of posting. other ms packages in project running latest version (demo project linked below updated reflect this).

testing

the initial testing made using loadster running against load-balanced iis 7.5 setup on server 2008 r2 .net 4.0.30319. i'm replicating locally on iis 7.5 on windows 7 .net 4.5.50709 using apache bench.

ab -n 500 -c 25 -p testdata.post -t "application/json" http://localhost/modelbindingfail/api/users/foo 

where testdata.post contains

{ "name":"james" } 

with testing i'm seeing 1 failure 500 requests, ~0.2%.

next steps...

i've put demo project on github if want try youself, though besides i've posted above it's standard empty web api project.

also happy try out suggestions or post more information. thanks!

i'm still investigating root cause of far gut feeling continuewith() being executed in different context, or @ point request stream has been disposed or (once figure out sure update paragraph).

in terms of fixes, i've road tested 3 can handle 500 requests no errors.

the simplest use task.result, have issues (it can apparently cause deadlocks, although ymmv).

protected override task<httpresponsemessage> sendasync(httprequestmessage request, cancellationtoken cancellationtoken) {     var result = request.content.readasstringasync().result;     return base.sendasync(request, cancellationtoken); } 

next, can ensure chaining continuations avoid ambiguity context, quite ugly (and i'm not 100% sure if side effect free):

protected override task<httpresponsemessage> sendasync(httprequestmessage request, cancellationtoken cancellationtoken) {     var result = request.content.readasstringasync().continuewith(task =>     {         /* stuff task.result */     });      return result.continuewith(t => base.sendasync(request, cancellationtoken)).unwrap(); } 

finally, optimal solution appears make use of async/await sweep away threading nasties, may issue if stuck on .net 4.0.

protected override async task<httpresponsemessage> sendasync(httprequestmessage request, cancellationtoken cancellationtoken) {     var content = await request.content.readasstringasync();     debug.writeline(content);     return await base.sendasync(request, cancellationtoken); } 

Comments

Popular posts from this blog

c# - Send Image in Json : 400 Bad request -

jquery - Fancybox - apply a function to several elements -

An easy way to program an Android keyboard layout app -