ASP.NET Core 2.2:. Twenty-format data Action returns handling mechanism

Previous chapter talked about how the system will request submitted by the client data format into a form that we want and bind to the corresponding parameters, chapter talk about its "reverse course" and how the request in accordance with the results the client wants the format go back. ( ASP.NET Core Series Catalog )

A common type of return

The system default template to generate the Home / Index this Action, the return to a Html page Why return it when requested it? Apart from this, there are JSON, text and other types of systems is how to deal with these different types of it?

First, for example several common return type, and a request by Fiddler these few examples look at the results, related to a class called Book, code:

    public class Book
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }

1.ViewResult type

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Returns a Html Page, Content-Type is: text / html; charset = utf-8

2.string type

    public string GetString()
    {
        return "Hello Core";
    }

Return the string "Hello Core", Content-Type is: text / plain; charset = utf-8

3.JSON type

    public JsonResult GetJson()
    {
        return new JsonResult(new Book() { Code = "1001", Name = "ASP" });
    }

Return JSON, is:

{"code":"1001","name":"ASP"}

Content-Type值为:Content-Type: application/json; charset=utf-8

4. Direct return entity type

public Book GetModel()
{
      return new Book() { Code = "1001", Name = "ASP" };
}

Also return JSON, is:

{"code":"1001","name":"ASP"}

Content-Type value is the same: Content-Type: application / json; charset = utf-8

5.void type

 public void GetVoid()
 {
 }

No results are returned and no Content-Type value.

Let's look at a few asynchronous method:

6.Task type

    public async Task GetTaskNoResult()
    {
        await Task.Run(() => { });
    }

And void types, no results are returned and no Content-Type value.

7.Task <string> Type

    public async Task<string> GetTaskString()
    {
        string rtn = string.Empty;
        await Task.Run(() => { rtn = "Hello Core"; });

        return rtn;
    }

And string types, return the string "Hello Core", Content-Type is: text / plain; charset = utf-8

8.Task <JsonResult> Type

    public async Task<JsonResult> GetTaskJson()
    {
        JsonResult jsonResult = null;
        await Task.Run(() => { jsonResult = new JsonResult(new Book() { Code = "1001", Name = "ASP" }); });
        return jsonResult;
    }

As with the type of JSON, JSON return, is:

[{"code":"1001","name":"ASP"},{"code":"1002","name":"Net Core"}]

Content-Type值为:Content-Type: application/json; charset=utf-8

There are other types, listed here temporarily, to sum up:

  1. Return results available, Html page, ordinary strings, JSON string a few.
  2. Content-Type corresponding free type, text / html, text / plain, application / json several.
  3. Action results returned asynchronous, synchronous and corresponding return consistent results Action type.

The next section we look at how the system is to deal with these different types.

Second, the mechanism to resolve internal processing

1. Overall Flow

By following diagram look at the overall process:

 

figure 1

This involves three parts:

The first portion, in the generation phase of the invoker. In Chapter 14, talk generated invoker of the time, talking about the acquisition of Action of performers, it is from a series of system-defined XXX screening ResultExecutor out, although they called XXX performer ResultExecutor, but they are of Action rather than ActionResult subclass of performers, all of ActionMethodExecutor. In Action synchronous or return type filters, particularly this part as shown in Figure 14-2 and Action of asynchronous XXX screening logic part and a listing ResultExecutor later. In Figure 17-1, a filter corresponding to the requested Action XXX ResultExecutor, In terms of Home / Index Case Action this default, this XXX ResultExecutor should be SyncActionResultExecutor.

The second part of the processing stage Action Filters. This section see 16.5 Filter execution here is just an example to Action Filter Action Filter talked about the process of implementation and the Action to be executed. At this stage, the Execute method calls above SyncActionResultExecutor of screening to perform Home / Index this Action. The results returned a IActionResult.

The third part of the processing stage Result Filters. This stage and Action Filters logic is similar, except that the former is the core of the implementation of Action, which is the core of the implementation of the results of the Action. And both divided OnExecuting OnExecuted two methods, both methods are performed before and after performing the method of its corresponding core.

The whole process is like, look at the details below.

2. ActionMethodExecutor selection and execution of

Why is the first part of the system to define so many XXX ResultExecutor and a more appropriate screening when requested XXX ResultExecutor it? Action from the screening rules are synchronous, asynchronous, and return type Action of view, so many XXX ResultExecutor is to deal with different types of Action.

Still with Home / Index, for example, in screening XXX ResultExecutor when the final result is returned SyncActionResultExecutor. Its code is as follows:

private class SyncActionResultExecutor : ActionMethodExecutor
{
     public override ValueTask<IActionResult> Execute(
          IActionResultTypeMapper mapper,
          ObjectMethodExecutor executor,
          object controller,
          object[] arguments)
          {
                var actionResult = (IActionResult)executor.Execute(controller, arguments);
                EnsureActionResultNotNull(executor, actionResult);
                return new ValueTask<IActionResult>(actionResult);
          }

     protected override bool CanExecute(ObjectMethodExecutor executor)
                => !executor.IsMethodAsync && typeof(IActionResult).IsAssignableFrom(executor.MethodReturnType);
}

XXX the CanExecute ResultExecutor method of screening conditions, by this method it is determined whether the current request for the target Action. It is not a requirement of this Action asynchronous and returns the result type is derived from the IActionResult. The Home / Index Action this identification result returned IActionResult, actually () This method returns through View, this method returns the type of the actual result is IActionResult derived class ViewResult. Such a derived class and the usual JsonResult ContentResult and so on, they have inherited the ActionResult, and ActionResult achieved IActionResult interface. So if an Action is synchronized and the result is JsonResult ContentResult or when the corresponding XXX ResultExecutor is SyncActionResultExecutor.

In the second part, is in the implementation of the Action XXX Execute method ResultExecutor, it will further call the Execute method ObjectMethodExecutor. Virtually all of the Action is performed by the Execute method ObjectMethodExecutor performed. The large number of XXX action ResultExecutor method is to call this method and returns the results to be verified and processed. For example SyncActionResultExecutor will ensure that the results returned by the EnsureActionResultNotNull method can not be empty.

If you are the type of sting? It corresponds SyncObjectResultExecutor, code is as follows:

private class SyncObjectResultExecutor : ActionMethodExecutor
{
    public override ValueTask<IActionResult> Execute(
        IActionResultTypeMapper mapper,
        ObjectMethodExecutor executor,
        object controller,
        object[] arguments)
    {
        // Sync method returning arbitrary object
        var returnValue = executor.Execute(controller, arguments);
        var actionResult = ConvertToActionResult(mapper, returnValue, executor.MethodReturnType);
        return new ValueTask<IActionResult>(actionResult);
    }

    // Catch-all for sync methods
    protected override bool CanExecute(ObjectMethodExecutor executor) => !executor.IsMethodAsync;

}

Since the string is not IActionResult subclass, it is processed by ConvertToActionResult will return results returnValue method.

private IActionResult ConvertToActionResult(IActionResultTypeMapper mapper, object returnValue, Type declaredType)
{
    var result = (returnValue as IActionResult) ?? mapper.Convert(returnValue, declaredType);
    if (result == null)
    {
        throw new InvalidOperationException(Resources.FormatActionResult_ActionReturnValueCannotBeNull(declaredType));
    }

    return result;
 }

If returnValue is a subclass of IActionResult Returns returnValue, or call a Convert method will change if the returnValue:

public IActionResult Convert(object value, Type returnType)
{
    if (returnType == null)
    {
        throw new ArgumentNullException(nameof(returnType));
    }

    if (value is IConvertToActionResult converter)
    {
        return converter.Convert();
    }

    return new ObjectResult(value)
    {
        DeclaredType = returnType,
    };
}

This method will determine whether to implement the returnValue IConvertToActionResult interface if the interface is the Convert method calls into IActionResult type, otherwise will returnValue packaged as ObjectResult. ObjectResult also ActionResult subclass of. There ActionResult <T> type is such Hereinafter, the ordinary assembly presentation.

So, for different types of Action, a variety of system settings XXX ResultExecutor to deal with, no matter what the final result will be converted to IActionResult type. IActionResult facilitate performed in the third section 17-1 shown in FIG.

On a number of different lists of Action, their treatment here is not explained. Look at Figure 17-2 through their processing results:

 

figure 2

There is no void type mentioned, which itself does not return results, but it will be assigned a result EmptyResult, it is also the ActionResult subclasses.

FIG 2 is divided into two rows of broken lines three lines, the first line are substantially introduced, the second line is the first line corresponding to an asynchronous method, the common return describes a class when said return these asynchronous methods the results and the corresponding synchronization procedure is the same. However it is seen by FIG. 2, the processing thereof XXX ResultExecutor method is not the same.

The third row ActionResult <T> type is introduced in ASP.NET Core 2.1, which supports IActionResult subclasses also supports a specific type such as string and similar Book.

public sealed class ActionResult<TValue> : IConvertToActionResult
{
    public ActionResult(TValue value)
    {
        if (typeof(IActionResult).IsAssignableFrom(typeof(TValue)))
        {
            var error = Resources.FormatInvalidTypeTForActionResultOfT(typeof(TValue), "ActionResult<T>");

            throw new ArgumentException(error);
        }

        Value = value;
    }

    public ActionResult(ActionResult result)
    {
        if (typeof(IActionResult).IsAssignableFrom(typeof(TValue)))
        {
            var error = Resources.FormatInvalidTypeTForActionResultOfT(typeof(TValue), "ActionResult<T>");
            throw new ArgumentException(error);
        }

        Result = result ?? throw new ArgumentNullException(nameof(result));
    }

    /// <summary>
    /// Gets the <see cref="ActionResult"/>.
    /// </summary>
    public ActionResult Result { get; }

    /// <summary>
    /// Gets the value.
    /// </summary>
    public TValue Value { get; }
 
    public static implicit operator ActionResult<TValue>(TValue value)
    {
        return new ActionResult<TValue>(value);
    }

    public static implicit operator ActionResult<TValue>(ActionResult result)
    {
        return new ActionResult<TValue>(result);
    }

    IActionResult IConvertToActionResult.Convert()
    {
        return Result ?? new ObjectResult(Value)
        {
            DeclaredType = typeof(TValue),
        };
    }
}

TValue does not support IActionResult its subclasses. Its value if IActionResult subclasses, will be assigned to the Result property, otherwise it will be assigned to the Value property. It implements IConvertToActionResult interfaces, expect just explain Convert method when the string type to be processed used. When returning results achieved IConvertToActionResult this interface, it invokes its Convert method for conversion. Convert it is to first determine whether its value is a subclass IActionResult after if the value is returned, otherwise the return value is converted to ObjectResult.

Therefore, FIG. 2 ActionResult <T> results return type is added is to subclass the meaning quoted IActionResult result types may be directly returned, there may be ObjectResult the type of string, and a particular type of such encapsulated Book.

3. Result Filter execution

Results are unitary after IActionResult, into the third part shown in Fig. The main content of this section there are two, namely the implementation and execution Result Filters IActionResult of. Result Filter and also OnResultExecuting OnResultExecuted two methods were performed before and after the execution IActionResult.

A custom MyResultFilterAttribute, code is as follows:

    public class MyResultFilterAttribute : Attribute, IResultFilter
    {
        public void OnResultExecuted(ResultExecutedContext context)
        {
            Debug.WriteLine("HomeController=======>OnResultExecuted");
        }

        public void OnResultExecuting(ResultExecutingContext context)
        {
            Debug.WriteLine("HomeController=======>OnResultExecuting");
        }
    }

Examples of the first to register it in JSON:

    [MyResultFilter]
    public JsonResult GetJson()
    {
        return new JsonResult(new Book() { Code = "1001", Name = "ASP" });
    }

You can see this output:

HomeController=======>OnResultExecuting

……Executing JsonResult……

HomeController=======>OnResultExecuted

In OnResultExecuting may be provided by context.Cancel = true; cancel implementation behind.

    public  void the OnResultExecuting (ResultExecutingContext context) 
    { 
        // code to verify slightly 
        context.Cancel = to true ; 
        Debug.WriteLine ( " the HomeController =======> the OnResultExecuting " ); 
    }

As you look at the output of the output

HomeController=======>OnResultExecuting

And returns JSON result is no longer a value, and returns the result as well as Content-Type are all empty. So after this setting, IActionResult and OnResultExecuted no longer be executed.

In addition to this can be done before some verification IActionResult execution, you can also do some simple operation HttpContext.Response, such as adding a Header values:

public void OnResultExecuting(ResultExecutingContext context)
{
    context.HttpContext.Response.Headers.Add("version", "1.2");
    Debug.WriteLine("HomeController=======>OnResultExecuting");
}

In addition to the results outside the normal return JSON, Header will appear in the new version added

Content-Type: application/json; charset=utf-8
version: 1.2

Look again OnResultExecuted method, which is performed after IActionResult execution. Because in this method is performed, the results have been sent to the client request requesting client, so the operation can be done here some log category. For example, if an abnormality occurs in this method:

  public void OnResultExecuted(ResultExecutedContext context)
  {
      throw new Exception("exception");
      Debug.WriteLine("HomeController=======>OnResultExecuted");
  }

Result of the request will still return to the normal JSON, but the output is not the same to see

HomeController=======>OnResultExecuting
……
System.Exception: exception

Abnormal, Debug back output is not performed. But the correct result is returned to the client.

Result Filter introduction is over, look at the core of the implementation of IActionResult.

4. IActionResult of execution

In the case State.ResultInside stage ResourceInvoker will call the execution method InvokeResultAsync IActionResult. Parameters IActionResult result will be called ExecuteResultAsync method for performing the method.

protected virtual async Task InvokeResultAsync(IActionResult result)
{
    var actionContext = _actionContext;
    _diagnosticSource.BeforeActionResult(actionContext, result);
    _logger.BeforeExecutingActionResult(result);


    try
    {
        await result.ExecuteResultAsync(actionContext);
    }
    finally
    {
        _diagnosticSource.AfterActionResult(actionContext, result);
        _logger.AfterExecutingActionResult(result);
    }
}

Figure 2 shows that, although the results of all types of Action are converted into IActionResult, but there are differences in their nature. So this IActionResult types of parameters result may actually be JsonResult, ViewResult, EmptyResult and other specific types. The following example is still the first to JSON example of view, it returns a JsonResult. Here will call JsonResult of ExecuteResultAsync method, JsonResult code is as follows:

public class JsonResult : ActionResult, IStatusCodeActionResult
{
   //部分代码略
    public override Task ExecuteResultAsync(ActionContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var services = context.HttpContext.RequestServices;
        var executor = services.GetRequiredService<JsonResultExecutor>();

        return executor.ExecuteAsync(context, this);
    }
}

In its method obtains JsonResultExecutor ExecuteResultAsync dependency injection set, to invoke the JsonResultExecutor ExecuteAsync method of performing work behind. JsonResultExecutor code is as follows:

public class JsonResultExecutor
{
//部分代码略
    public virtual async Task ExecuteAsync(ActionContext context, JsonResult result)
    {
        //验证代码略
        var response = context.HttpContext.Response;
  ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
            result.ContentType,
            response.ContentType,
            DefaultContentType,
            out var resolvedContentType,
            out var resolvedContentTypeEncoding);

        response.ContentType = resolvedContentType;

        if (result.StatusCode != null)
        {
            response.StatusCode = result.StatusCode.Value;
        }

        var serializerSettings = result.SerializerSettings ?? Options.SerializerSettings;
        Logger.JsonResultExecuting(result.Value);
        using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
        {
            using (var jsonWriter = new JsonTextWriter(writer))
            {
                jsonWriter.ArrayPool = _charPool;
                jsonWriter.CloseOutput = false;
                jsonWriter.AutoCompleteOnClose = false;
                var jsonSerializer = JsonSerializer.Create(serializerSettings);
                jsonSerializer.Serialize(jsonWriter, result.Value);

            }
            // Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
            // buffers. This is better than just letting dispose handle it (which would result in a synchronous write).

            await writer.FlushAsync();
        }
    }
}

JsonResultExecutor effect ExecuteAsync method is to convert the values ​​to JSON JsonResult serial write context.HttpContext.Response. Body in. So far JsonResult executed.

ViewResult corresponding ViewExecutor will be performed, will generate a Html page by respective rules.

The method EmptyResult ExecuteResult is empty, so it will not return anything.

    public class EmptyResult : ActionResult
    {
        /// <inheritdoc />
        public override void ExecuteResult(ActionContext context)
        {

        }
    }

5. Set Preview

The results returned for the above types of fixed format, JsonResult will JSON format, ViewResult returns Html format.

However, seen from the first example, the string type string type will return a string, and such an entity type but it will return Book JSON.

Both types after the implementation, are packaged in a ObjectResult be seen from Figure 2, then ObjectResult in the implementation of and how is converted into a string and JSON formats it?

The next chapter continues this topic.

Guess you like

Origin www.cnblogs.com/FlyLolo/p/ASPNETCore2_20.html