private Type GetDeclaredReturnType(ControllerActionDescriptor action) { var declaredReturnType = action.MethodInfo.ReturnType; if (declaredReturnType == typeof(void) || declaredReturnType == typeof(Task) || declaredReturnType == typeof(ValueTask)) { return(typeof(void)); } // Unwrap the type if it's a Task<T>. The Task (non-generic) case was already handled. var unwrappedType = declaredReturnType; if (declaredReturnType.IsGenericType && (declaredReturnType.GetGenericTypeDefinition() == typeof(Task <>) || declaredReturnType.GetGenericTypeDefinition() == typeof(ValueTask <>))) { unwrappedType = declaredReturnType.GetGenericArguments()[0]; } // If the method is declared to return IActionResult or a derived class, that information // isn't valuable to the formatter. if (typeof(IActionResult).IsAssignableFrom(unwrappedType)) { return(null); } // If we get here, the type should be a user-defined data type or an envelope type // like ActionResult<T>. The mapper service will unwrap envelopes. unwrappedType = _mapper.GetResultDataType(unwrappedType); return(unwrappedType); }