MVC4 WebAPI (2) -- how Web APIs work, mvc4webapi

Source: Internet
Author: User

MVC4 WebAPI (2) -- how Web APIs work, mvc4webapi

In the previous article, I learned how to set up basic WebAPI applications. Someone immediately thought of some problems:
1. File Transfer between the client and WebService
2. Security Control on the client or server
To solve these problems, you need to understand the basic working methods of WebAPI.

(1) Class used in WebAPI

As we all know in MVC, The HttpRequest and HttpResponse classes are used to obtain requests and Response, and two other classes are used in WebAPI:
HttpRequestMessage and HttpResponseMessage are used to encapsulate Requset and Response respectively. In addition to these two classes, there is also a common abstract class: HttpMessageHandler, used to filter and process HttpRequestMessage and HttpResponseMessage

(2) solve the first problem

In fact, the first problem was raised was related to the client. If the client request is submitted by hand, for example, the request encapsulated by HttpClient, before the file is passed, we will perform a serialization, convert it to a binary array, and transfer it over the network. In this case, in the Action parameter in the Controller, we only need to receive the binary array type object.
But if the client is a Web Form, for example, if we submit a Form to the Action of the specified Controller, what type of parameters does this Action need to receive?
Or let's ask another question. If I submit a Web Form to a WebAPI Action, how can I retrieve the data in this Form?
In practice, we should think: the reason why the parameter set for our Action can be assigned a value is that in the WebAPI architecture, when the Action is called, the data in the HTTP request is parsed and assigned to the parameters in the Action, respectively, if this is the case, we only need to get the HTTP request in the Action and then directly obtain the data in the request to solve the above problem.
This idea is correct, but the HTTP Request is not the original HTTP Request, but has been converted into HttpRequestMessage. In the Action, we can directly call the base. requet to get the HttpRequestMessage instance. With this instance, we can retrieve the data in the HTTP request as needed.

2.1 obtain common form data from RequestMessage

The normal form here refers to a form that does not contain File, that is, the form's enctype value is not multipart/form-data. In this case, the form data is transmitted in Json by default.
The following page

<form name="form" action="~/api/FormSubmit?key=11234" method="post">    <input type="text" name="key" id="txtKey" />    <br />    <input type="text" name="value" id="txtValue" />    <br />        <input type="submit" id="btnSubmit" value="Submit" />     </form>

The captured request is

The corresponding Action to be submitted is:

        [HttpPost]        public async void submitForm()        {            StringBuilder sb = new StringBuilder();            HttpContent content = Request.Content;            JsonObject jsonValue = await content.ReadAsOrDefaultAsync<JsonObject>();            foreach (var x in jsonValue)            {                sb.Append(x.Key);                string va ;                if (x.Value.TryReadAs<string>(out va))                {                    sb.Append(va);                }            }        }

In this way, the Json value {"key": "123", "value": "123"} after sb processing is: key123value123.

 

Note: The async and await keywords are used in this action. These new keywords are mainly used for multithreading. In MVCAPI design, most of the methods are designed to be similar to the following methods:

public static Task<T> ReadAsOrDefaultAsync<T>(this HttpContent content);

The return value is a Task. This method of returning a new thread can improve the system's response capability, but the multi-threaded value will cause inconvenience to coding, therefore, the New Keyword await is used to block the current thread and obtain the returned value of the target thread. After the await keyword is used in the method body, the method must be declared as async to indicate that the method is asynchronous, the returned value must be void or be encapsulated in a Task.
Of course, if you do not like this method, the preceding action can also be written as follows:

            Task readTask = content.ReadAsOrDefaultAsync<JsonObject>().ContinueWith((task) => { jsonValue = task.Result; });            readTask.Wait();


2.2 obtain multipart form data from RequestMessage
Rewrite the view page

<form name="form" action="~/api/FormSubmit?key=11234" method="post" enctype="multipart/form-data" >    <input type="text" name="key" id="txtKey" />    <br />    <input type="text" name="value" id="txtValue" />    <br />    <input type="file" name="file" id="upFile" />    <br />    <input type="submit" id="btnSubmit" value="Submit" /></form>

The captured request is


The file content is captured and parsed as a string. Of course, if I upload another non-text file, the file will be converted to a binary array.
At this time, if we do not change the action and call it directly, an error will occur. The reason is obvious: the HTTP message content cannot be converted to JSON, in this case, we need to parse the form message into another format.

                IEnumerable<HttpContent> bodyparts = await content.ReadAsMultipartAsync();                foreach (var bodypart in bodyparts)                {                    string name;                    name = bodypart.Headers.ContentDisposition.Name;                    sb.Append(name + ":");                    if (bodypart.Headers.Contains("filename"))                    {                        Stream stream = await bodypart.ReadAsStreamAsync();                        StreamReader reader = new StreamReader(stream);                        sb.Append(reader.ReadToEnd());                        sb.Append("----");                    }                    else                    {                        string val = await bodypart.ReadAsStringAsync();                        sb.Append(val);                        sb.Append("----");                    }                }

The resulting sb value is:

{"Key": 123 ---- "value": 123 ---- "file": ****** {file content }*****----}
The Integrated Action is

        [HttpPost]        public async void submitForm()        {            StringBuilder sb = new StringBuilder();            HttpContent content = Request.Content;            if (content.IsMimeMultipartContent())            {                IEnumerable<HttpContent> bodyparts = await content.ReadAsMultipartAsync();                foreach (var bodypart in bodyparts)                {                    string name;                    name = bodypart.Headers.ContentDisposition.Name;                    sb.Append(name + ":");                    if (bodypart.Headers.Contains("filename"))                    {                        Stream stream = await bodypart.ReadAsStreamAsync();                        StreamReader reader = new StreamReader(stream);                        sb.Append(reader.ReadToEnd());                        sb.Append("----");                    }                    else                    {                        string val = await bodypart.ReadAsStringAsync();                        sb.Append(val);                        sb.Append("----");                    }                }            }            else            {                JsonObject jsonValue = await content.ReadAsOrDefaultAsync<JsonObject>();                foreach (var x in jsonValue)                {                    sb.Append(x.Key);                    string va;                    if (x.Value.TryReadAs<string>(out va))                    {                        sb.Append(va);                    }                }            }        }

 

(3) how WebAPI works

It is not so easy to solve the second problem. We need to have a deeper understanding of how WebAPI works.
In fact, for WebAPI, it was initially designed to be the same as that for WCF: two sets of structures, client and server. We haven't mentioned the client yet, this is because our request is encapsulated into an HTTP request or an HTTP response, such as AJAX and Form submission.

Here, we will first provide a response workflow for the server to give you a general understanding.

(I had to add a watermark to the image because I was taken away from my hard work and didn't give a link to the original text. It was really bad .. hope it will not affect reading ...)
Due to the limited image size, all httprequestmessages are abbreviated as HttpRequestMsg, and HttpResponseMessage is abbreviated as HttpResponseMsg.

You can see that the HTTP request is first transmitted to the HOST. If the WebAPI is hosted on IIS, the HOST is IIS, the HOST is incapable and does not have to process the request. The request is forwarded to the HttPServer through the HOST, which has entered the processing scope of WebAPI. The HttpServer is System. net. A class in HTTP. Through HttpServer, requests are encapsulated into the request bearer class in WebAPI: HttpRequestMessage. The encapsulated request can be processed by a series of Custom Handler, the handler concatenates a pipeline and finally the request is passed to HttpControlDispather. This class determines the Action that the request will be forwarded to the specific Controller through searching the route table.

The Client-side processing is similar to that of the server:

In fact, according to Microsoft, they are designed into a similar but independent structure.

ASP.NET Web API has a pipeline for processing HTTP messages on both the client and server. The client and server sides are designed to be symmetrical but independent; you can use each half by itself. Both sides are built on some common objects:

  • HttpRequestMessage represents the HTTP request.
  • HttpResponseMessage represents the HTTP response.
  • HttpMessageHandler objects process the request and response.

As shown in the figure, Handlers pipeline is finally transmitted to HttpClientHandler on the client, and he is responsible for the conversion from HttpRequestMessage to HTTP request.

Here we will only describe the Request and Response is similar to it.

(4) solve the second problem

We can see from this that we can solve the second problem directly in Handler PipeLine. This AOP-style filter (interceptor) is widely used in the security verification of REST Webservice, generally, you are more willing to add the authentication field to the HTTP header or the HTTP request URL for authentication. The following is a small example of adding authentication information to the Http header.

4.1 Client
The client's customhandler is used to add authentication information to the header

    class RequestUpHandler : DelegatingHandler    {        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)        {            request.Headers.Add("key", "11234");            return base.SendAsync(request, cancellationToken);        }    }

Note:
1. customhandler inherits from the DelegatingHandler class. As mentioned above, the client and server of WebAPI are designed to correspond to each other. Therefore, both the client and the server are inherited from the DelegatingHandler class.
2. The sendAsync method of DelegatingHandler is the method that will be called when processing and receiving requests. The return value of this method is HttPResponseMessage, And the received value is HttpRequestMessage, which meets our general cognition.
3. At the end of the method, calling base. SendAsync is to pass the Request to other mmhandler of the pipeline and obtain the returned value. Because this method does not contain the processing logic of Response, you only need to directly
Return Value directly
Client main program

        static void Main(string[] args)        {            HttpClient client = new HttpClient(new RequestUpHandler() { InnerHandler = new HttpClientHandler() });            HttpResponseMessage response = client.GetAsync("http://localhost:60023/api/FormSubmit").Result;            response.Content.ReadAsAsync<string>().ContinueWith((str) => { Console.WriteLine(str.Result); });            Console.Read();        }

The main program of the customer creates an HttpClient. The HttpClient can accept a parameter, which is CustomHandler. Here we embed the RequestUpHandler we defined, the Request header is used to embed the identity verification code. CustomHandler embeds the built-in next mmhandler through the InnerHandler attribute. Because there is no next CustomerHandler, we directly embed HttpClientHandler to convert HttpRequestMessage to HTTP request, and convert HTTP Response to httprequestsemessage.

4.2 Server
The customHandler of the server is used to parse the authentication code in the HTTP header.

        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)        {            int matchHeaderCount = request.Headers.Count((item) =>            {                if ("key".Equals(item.Key))                {                    foreach (var str in item.Value)                    {                        if ("11234".Equals(str))                        {                            return true;                        }                    }                }                return false;            });            if (matchHeaderCount>0)            {                return base.SendAsync(request, cancellationToken);            }            return Task.Factory.StartNew<HttpResponseMessage>(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); });        }

Note: The code processing logic is simple: if the identity verification code matches successfully, it passes through base. sendAsync continues to send the request downward. Otherwise, the request is directly interrupted and a 403 response code is returned, indicating that the request has no permission.
Note that the return value of SendAsync needs to be encapsulated in the Task, so you need to use Task. Factory. StartNew to include the return value in the Task.

Inject customHandler into HOST
In this example, the WebAPI HOST is on IIS, so we only need to define the CustomHandler we define in Application_Start.

Protected void Application_Start () {// omit other logic code GlobalConfiguration. Configuration. MessageHandlers. Add (new HttpUrlHandler ());}

Because WebAPI Host is on IIS, we do not need to manually process HttpServer and HttpControllerDispatcher.

After the above processing is added, if there is no authentication code for the request, the following response will be obtained:

 

**************************************** **************************************
Author: Wang Kun
Source: http://www.cnblogs.com/wk1234
This article is copyrighted by Wang Kun and the blog Park. You are welcome to reprint it, but please indicate the source.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.