git clone https://github.com/nil4/MvcCoreObjectResultRepro.git
cd MvcCoreObjectResultRepro
dotnet restore
dotnet run
Open http://localhost:5000/ in your browser.
The page displays {"a":"test"}, i.e. only the ObjectResult value is returned to the client.
The page displays {"value":{"a":"test"},"formatters":[],"contentTypes":[],"declaredType":null,"statusCode":null} instead, i.e. all the properties of ObjectResult are returned to the client, not just the value.
The incorrect behavior is new to 1.0 RTM, and used to work fine in RC2.
Yep, this is definitely not intended. Thanks for the repro
I updated the repro code to test other ActionResult subclasses. Here are the test URLs and results:
/201 (CreatedResult):
{"location":"/","value":null,"formatters":[],"contentTypes":[],"declaredType":null,"statusCode":201} content, status code = 200, Location header missing./204 (NoContentResult):
{"statusCode":204} content, status code = 200./302 (LocalRedirectResult):
{"permanent":false,"url":"/","urlHelper":null} content, status code = 200, Location header missing./304 (StatusCodeResult):
{"statusCode":304} content, status code = 200./400 (BadRequestResult):
{"statusCode":400} content, status code = 200./404 (NotFoundObjectResult):
{"value":null,"formatters":[],"contentTypes":[],"declaredType":null,"statusCode":404} content, status code = 400.The problem is here and here. Both of these cases should have a conditional cast to IActionResult.
var resultAsObject = await _executor.ExecuteAsync(_controller, arguments);
result = new ObjectResult(resultAsObject)
{
DeclaredType = _executor.TaskGenericType,
};
Should change to:
var resultAsObject = await _executor.ExecuteAsync(_controller, arguments);
result = resultAsObject as IActionResult;
if (result == null)
{
result = new ObjectResult(resultAsObject)
{
DeclaredType = _executor.TaskGenericType,
};
}
If you want a workaround, I'd suggest the following:
public class Fix4960ActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) { }
public void OnActionExecuted(ActionExecutedContext context)
{
var objectResult = context.Result as ObjectResult;
if (objectResult?.Value is IActionResult)
{
context.Result = (IActionResult)objectResult.Value;
}
}
}
@rynowak thank you for the workaround, will test and report back.
@rynowak looking at the code you linked to, it seems both cases deal with non-async method return values. The code I posted is a minimal repro derived from an actual project where all actions are async, and those are affected as well. Thought I should mention this now to avoid a partial fix.
Lines 641- 648 is the synchronous case. The fix for that case is the same as the sample I provided. This code has had a lot of perf work done on it, so it's a little confusing :laughing:
@rynowak workaround confirmed, thank you!
/cc @Eilon @DamianEdwards FYI
@javiercn @Eilon @danroth27 The issue was reported three weeks ago but the fix is not yet merged. Is there still a chance of this making it into v1.0.1 or v1.1.0?
It's currently slated to be fixed in 1.1.0
Done?
Most helpful comment
The problem is here and here. Both of these cases should have a conditional cast to
IActionResult.Should change to:
If you want a workaround, I'd suggest the following: