网页API操作参数是间歇性空 [英] Web API action parameter is intermittently null

查看:82
本文介绍了网页API操作参数是间歇性空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

相关问题:<一href=\"http://stackoverflow.com/questions/16091024/web-api-apicontroller-put-and-post-methods-receive-null-parameters-intermittentl\">Web API ApiController PUT和POST方法接收空参数间歇

在负载测试我注意到有很多空引用异常作为参数张贴到一个动作时,被空的结果,现有的Web API项目。

While load testing an existing Web API project I noticed a lot of null reference exceptions as a result of a parameter being null when posting to an action.

原因似乎是注册的登录请求,在开发环境中运行时的自定义消息处理程序。删除此处理程序解决问题。

The cause seems to be a custom message handler registered to log requests while running in dev environments. Removing this handler resolves the issue.

据我了解,在网页API我只能读取请求体一次,阅读它总是会引起我的参数为空的模型绑定就无法成行。出于这个原因,我使用ReadAsStringAsync()方法ContinueWith读取身上。看起来这是很奇怪的请求〜0.2%(使用Apache台本地调试期间)的行为。

I understand that in Web API I can only read the request body once and that reading it would always cause my parameter to be null as model binding wouldn't be able to take place. For that reason I'm using the ReadAsStringAsync() method with ContinueWith to read the body. It looks like this is behaving oddly in ~0.2% of requests (during local debugging using Apache Bench).

在最基本的层面上我有以下几点:

At the most basic level I have the following:

public class User
{
    public string Name { get; set; }
}

API控制器

public class UsersController : ApiController
{
    [HttpPost]
    public void Foo(User user)
    {
        if (user == null)
        {
            throw new NullReferenceException();
        }
    }
}

消息处理程序

public class TestMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Content.ReadAsStringAsync().ContinueWith((task) =>
        {
            /* do stuff with task.Result */
        });

        return base.SendAsync(request, cancellationToken);
    }
}

这是在应用程序注册

...启动

GlobalConfiguration.Configuration.MessageHandlers.Add(new TestMessageHandler());

我使用的WebAPI 4.0.30506.0,最新在投寄时间。项目中的所有其它MS包也运行最新版本(现在以下链接更新以反映此演示项目)。

I'm using WebAPI 4.0.30506.0, the latest at the time of posting. All other MS packages in the project are also running the latest version (demo project linked below now updated to reflect this).

最初的测试使用 Loadster 针对运行作出了负载均衡的IIS 7.5的设置Server 2008 R2的上.NET 4.0.30319。我在本地IIS 7.5在Windows 7上使用.NET 4.5.50709使用Apache台复制此。

The initial testing was made using Loadster running against a load-balanced IIS 7.5 setup on Server 2008 R2 with .NET 4.0.30319. I'm replicating this locally on IIS 7.5 on Windows 7 with .NET 4.5.50709 using Apache Bench.

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

在这里testdata.post包含

where testdata.post contains

{ "Name":"James" }

有了这个测试我看到了500个请求大约1失败,所以〜0.2%。

With this testing I'm seeing roughly 1 failure for the 500 requests, so ~0.2%.

我已经把我的演示项目上 GitHub的如果你想尝试youself,虽然除了我已经张贴以上是一个标准的空的Web API项目。

I've put my demo project on GitHub if you want to try for youself, though besides what I've posted above it's a standard empty Web API project.

也乐于尝试任何建议或发布的详细信息。谢谢!

Also happy to try out any suggestions or post more information. Thanks!

推荐答案

我仍然在调查这一现象的根本原因,但到目前为止,我的直觉是,ContinueWith()是在不同的上下文中执行,或在点通过该请求流已被处置,或者类似的东西(一旦我明白这一点是肯定的,我会更新这个段落)。

I'm still investigating the root cause of this but so far my gut feeling is that ContinueWith() is being executed in a different context, or at a point by which the request stream has been disposed or something like that (once I figure that out for sure I will update this paragraph).

在修复方面,我赶紧道路测试了三种可以处理500个请求没有任何错误。

In terms of fixes, I've quickly road tested three that can handle 500 requests with no errors.

最简单的是只使用 task.Result ,但这但是有一些问题(可以的显然会导致死锁的,但情况因人而异)。

The simplest is to just use task.Result, this does however have some 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);
}

接下来,你可以确保你正确链接你的延续,以避免对环境的任何模糊性,但它是相当难看(我不是100%肯定,如果它是无副作用):

Next, you can ensure you are properly chaining your continuations to avoid any ambiguity about context, it is however quite ugly (and I'm not 100% sure if it is side effect free):

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    var result = request.Content.ReadAsStringAsync().ContinueWith(task =>
    {
        /* do stuff with task.Result */
    });

    return result.ContinueWith(t => base.SendAsync(request, cancellationToken)).Unwrap();
}

最后,最佳的解决方案似乎利用异步/的await到<一个href=\"http://stackoverflow.com/questions/15817594/should-i-use-continuewith-after-readasasync-in-delegatinghandler\">sweep离开任何线程脏东西的,显然这可能是一个问题,如果你坚持在.NET 4.0。

Finally, the optimal solution appears to make use of async/await to sweep away any threading nasties, obviously this may be an issue if you are 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);
}

这篇关于网页API操作参数是间歇性空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆