问题描述:

I see that there are multiple ways I can return data in Asp.Net Core controller when various results are possible:

1) object

[HttpPost]

public object RefreshToken()

{

if (Validate())

{

return new

{

token = CreateToken()

};

}

return HttpUnauthorized()

}

2) dynamic

[HttpPost]

public dynamic RefreshToken()

{

if (Validate())

{

return new

{

token = CreateToken()

};

}

return HttpUnauthorized()

}

3) IActionResult

[HttpPost]

public IActionResult RefreshToken()

{

if (Validate())

{

return new ObjectResult(new

{

token = CreateToken()

});

}

return HttpUnauthorized()

}

Are there any differences with these 3 approaches? Which one should be preferred?

网友答案:

If I correctly understand your question, the question is mostly about the style of writing the program, because all the options will be called in the same way.

MVC execute all the Actions in the same way. The line with invocationResult = actionMethodInfo.Invoke(instance, orderedActionArguments); (where I placed !!! in comments) calls the controller action:

public static Task<object> ExecuteAsync(
    MethodInfo actionMethodInfo,
    object instance,
    object[] orderedActionArguments)
{
    object invocationResult = null;
    try
    {
        invocationResult = actionMethodInfo.Invoke(instance, orderedActionArguments); // !!!
    }
    catch (TargetInvocationException targetInvocationException)
    {
        // Capturing the exception and the original callstack and rethrow for external exception handlers.
        var exceptionDispatchInfo = ExceptionDispatchInfo.Capture(targetInvocationException.InnerException);
        exceptionDispatchInfo.Throw();
    }

    return CoerceResultToTaskAsync(
        invocationResult,
        actionMethodInfo.ReturnType,
        actionMethodInfo.Name,
        actionMethodInfo.DeclaringType);
}

The results will be interpreted always as object (object invocationResult). Then the method CoerceResultToTaskAsync tests whether the type of return object is Task and if not, converts it to Task. Then the outer method (inside of var actionReturnValue = await ControllerActionExecutor.ExecuteAsync(...);) returns the value here and CreateActionResult tests in the lines (var actionResult = actionReturnValue as IActionResult; if (actionResult != null) { return actionResult; }) that the returned result have IActionResult interface. If not, then one returns new ObjectResult(...). See below the copy of the code

internal static IActionResult CreateActionResult(Type declaredReturnType, object actionReturnValue)
{
    if (declaredReturnType == null)
    {
        throw new ArgumentNullException(nameof(declaredReturnType));
    }

    // optimize common path
    var actionResult = actionReturnValue as IActionResult;
    if (actionResult != null)
    {
        return actionResult;
    }

    if (declaredReturnType == typeof(void) ||
        declaredReturnType == typeof(Task))
    {
        return new EmptyResult();
    }

    // Unwrap potential Task<T> types.
    var actualReturnType = GetTaskInnerTypeOrNull(declaredReturnType) ?? declaredReturnType;
    if (actionReturnValue == null &&
        typeof(IActionResult).GetTypeInfo().IsAssignableFrom(actualReturnType.GetTypeInfo()))
    {
        throw new InvalidOperationException(
            Resources.FormatActionResult_ActionReturnValueCannotBeNull(actualReturnType));
    }

    return new ObjectResult(actionReturnValue)
    {
        DeclaredType = actualReturnType
    };
}

In other words, all the code do the same. The first option, which returns object, and the last one will work identical. I personally would prefer to use the first option to have no explicit call of ObjectResult, but in other cases the last version with IActionResult is readable enough. It's more the matter of the taste.

相关阅读:
Top