您的位置:首页 > 编程语言 > ASP

ASP.NET Web API系列教程之 html表单 与服务器

2013-05-10 19:24 671 查看


5.1 HTTP Message Handlers

5.1 HTTP消息处理器

本文引自:http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

By Mike Wasson | February 13, 2012

作者:Mike Wasson |日期:2012-2-13

A message handler is a class that receives an HTTP request and returns an HTTP response. Message handlers derive from the abstract
[b]HttpMessageHandler
class.

消息处理器是一个接收HTTP请求并返回HTTP响应的类。消息处理器派生于HttpMessageHandler类。

Typically, a series of message handlers are chained together. The first handler receives an HTTP request, does some processing, and gives the request to the next handler. At some point, the response is created and goes back up
the chain. This pattern is called a delegating handler.

典型地,一系列消息处理器被链接在一起。第一个处理器接收HTTP请求、进行一些处理,并将该请求交给下一个处理器。在某个点上,创建响应并沿链返回。这种模式称为委托处理器(delegating handler)(如图5-1所示)。



图中:Request:请求,Delegating Handler:委托处理器,Inner Handler:内部处理器,Response:响应
图5-1. 消息处理器链

Server-Side Message Handlers

服务器端消息处理器

On the server side, the Web API pipeline uses some built-in message handlers:

在服务器端,Web API管线使用一些内建的消息处理器:

HttpServer gets the request from the host.

HttpServer获取从主机发来的请求。
HttpRoutingDispatcher dispatches the request based on the route.

HttpRoutingDispatcher(HTTP路由分发器)基于路由对请求进行分发。
HttpControllerDispatcher sends the request to a Web API controller.

HttpControllerDispatcher(HTTP控制器分发器)将请求发送给一个Web API控制器。

You can add custom handlers to the pipeline. Message handlers are good for cross-cutting concerns that operate at the level of HTTP messages (rather than controller actions). For example, a message handler might:

可以把自定义处理器添加到该管线上。对于在HTTP消息层面上(而不是在控制器动作上)进行操作的交叉关注,消息处理器是很有用的。例如,消息处理器可以:

Read or modify request headers.

读取或修改请求报头。
Add a response header to responses.

对响应添加响应报头。
Validate requests before they reach the controller.

在请求到达控制器之前验证请求。

This diagram shows two custom handlers inserted into the pipeline:

下图显示了插入到管线的两个自定义处理器(见图5-2):



图5-2. 服务器端消息处理器

On the client side, HttpClient also uses message handlers. For more information, see

HttpClient Message Handlers.

在客户端,HttpClient也使用消息处理器。更多信息参阅“Http消息处理器”(本系列教程的第3.4小节 — 译者注)。

Custom Message Handlers

自定义消息处理器

To write a custom message handler, derive from
System.Net.Http.DelegatingHandler
and override the SendAsync method. This method has the following signature:

要编写一个自定义消息处理器,需要通过System.Net.Http.DelegatingHandler进行派生,并重写SendAsync方法。该方法有以下签名:

Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken);


The method takes an HttpRequestMessage as input and asynchronously returns an
HttpResponseMessage. A typical implementation does the following:

该方法以HttpRequestMessage作为输入参数,并异步地返回一个HttpResponseMessage。典型的实现要做以下事:

Process the request message.

处理请求消息。
Call base.SendAsync to send the request to the inner handler.

调用base.SendAsync将该请求发送给内部处理器。
The inner handler returns a response message. (This step is asynchronous.)

内部处理器返回响应消息。(此步是异步的。)
Process the response and return it to the caller.

处理响应,并将其返回给调用者。

Here is a trivial example:

以下是一个价值不高的示例:

public class MessageHandler1 : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Debug.WriteLine("Process request");
// Call the inner handler.
// 调用内部处理器。
var response = await base.SendAsync(request, cancellationToken);
Debug.WriteLine("Process response");
return response;
}
}


The call to base.SendAsync is asynchronous. If the handler does any work after this call, use the await keyword, as shown.

base.SendAsync的调用是异步的。如果处理器在调用之后有工作要做,需使用await关键字,如上所示。

A delegating handler can also skip the inner handler and directly create the response:

委托处理器也可以跳过内部处理器,并直接创建响应:

public class MessageHandler2 : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Create the response.
// 创建响应。
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Hello!")
};

// Note: TaskCompletionSource creates a task that does not contain a delegate.
// 注:TaskCompletionSource创建一个不含委托的任务。
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response);   // Also sets the task state to "RanToCompletion"
// 也将此任务设置成“RanToCompletion(已完成)”
return tsc.Task;
}
}


If a delegating handler creates the response without calling base.SendAsync, the request skips the rest of the pipeline. This can be useful for a handler that validates the request (creating an error response).

如果委托处理器未调用base.SendAsync创建了响应,该请求会跳过管线的其余部分(如图5-3所示)。这对验证请求(创建错误消息)的处理器,可能是有用的。



图5-3. 被跳过的处理器

Adding a Handler to the Pipeline

向管线添加一个处理器

To add a message handler on the server side, add the handler to the
HttpConfiguration.MessageHandlers collection. If you used the "ASP.NET MVC 4 Web Application" template to create the project, you can do this inside the
WebApiConfig class:

要在服务器端添加消息处理器,需要将该处理器添加到HttpConfiguration.MessageHandlers集合。如果创建项目用的是“ASP.NET MVC 4 Web应用程序”模板,你可以在WebApiConfig类中做这件事:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());

// Other code not shown(未列出其它代码)...
}
}


Message handlers are called in the same order that they appear in
MessageHandlers collection. Because they are nested, the response message travels in the other direction. That is, the last handler is the first to get the response message.

消息处理器是按它们在MessageHandlers集合中出现的顺序来调用的。因为它们是内嵌的,响应消息以反方向运行,即,最后一个处理器最先得到响应消息。

Notice that you don't need to set the inner handlers; the Web API framework automatically connects the message handlers.

注意,不需要设置内部处理器;Web API框架会自动地连接这些消息处理器。

If you are
self-hosting, create an instance of the HttpSelfHostConfiguration class and add the handlers to the
MessageHandlers collection.

如果是“自托管”(本系列教程的第8.1小节 — 译者注)的,需要创建一个HttpSelfHostConfiguration类的实例,并将处事器添加到MessageHandlers集合。

var config = new HttpSelfHostConfiguration("http://localhost");
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());


Now let's look at some examples of custom message handlers.

现在,让我们看一些自定义消息处理器的例子。

Example: X-HTTP-Method-Override

示例:X-HTTP-Method-Override

X-HTTP-Method-Override is a non-standard HTTP header. It is designed for clients that cannot send certain HTTP request types, such as PUT or DELETE. Instead, the client sends a POST request and sets the X-HTTP-Method-Override
header to the desired method. For example:

X-HTTP-Method-Override是一个非标准的HTTP报头。这是为不能发送某些HTTP请求类型(如PUT或DELETE)的客户端而设计的。因此,客户端可以发送一个POST请求,并把X-HTTP-Method-Override设置为所希望的方法(意即,对于不能发送PUT或DELETE请求的客户端,可以让它发送POST请求,然后用X-HTTP-Method-Override把这个POST请求重写成PUT或DELETE请求 — 译者注)。例如:

X-HTTP-Method-Override: PUT


Here is a message handler that adds support for X-HTTP-Method-Override:

以下是添加了X-HTTP-Method-Override支持的一个消息处事器:

public class MethodOverrideHandler : DelegatingHandler
{
readonly string[] _methods = { "DELETE", "HEAD", "PUT" };
const string _header = "X-HTTP-Method-Override";

protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Check for HTTP POST with the X-HTTP-Method-Override header.
// 检查带有X-HTTP-Method-Override报头的HTTP POST。
if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
{
// Check if the header value is in our methods list.
// 检查其报头值是否在我们的方法列表中
var method = request.Headers.GetValues(_header).FirstOrDefault();
if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
{
// Change the request method.
// 修改请求方法
request.Method = new HttpMethod(method);
}
}
return base.SendAsync(request, cancellationToken);
}
}


In the SendAsync method, the handler checks whether the request message is a POST request, and whether it contains the X-HTTP-Method-Override header. If so, it validates the header value, and then modifies the
request method. Finally, the handler calls base.SendAsync to pass the message to the next handler.

SendAsync方法中,处理器检查了该请求消息是否是一个POST请求,以及它是否含有X-HTTP-Method-Override报头。如果是,它会验证报头值,然后修改请求方法。最后,处理器调用base.SendAsync,把消息传递给下一下处事器。

When the request reaches the HttpControllerDispatcher class,
HttpControllerDispatcher will route the request based on the updated request method.

当请求到达HttpControllerDispatcher类时,HttpControllerDispatcher会根据这个已被更新的请求方法对该请求进行路由。

Example: Adding a Custom Response Header

示例:添加自定义响应报头

Here is a message handler that adds a custom header to every response message:

以下是一个把自定义报头添加到每个响应消息的消息处理器:

// .Net 4.5
public class CustomHeaderHandler : DelegatingHandler
{
async protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("X-Custom-Header", "This is my custom header.");
return response;
}
}


First, the handler calls
base.SendAsync
to pass the request to the inner message handler. The inner handler returns a response message, but it does so asynchronously using a
Task<T> object. The response message is not available until
base.SendAsync
completes asynchronously.

首先,该处理器调用base.SendAsync把请求传递给内部消息处理器。内部处理器返回一条响应消息,但它是用Task<T>对象异步地做这件事的。在base.SendAsync异步完成之前,响应消息是不可用的。

This example uses the await keyword to perform work asynchronously after
SendAsync completes. If you are targeting .NET Framework 4.0, use the
Task.ContinueWith method:

这个示例使用了await关键字,以便在SendAsync完成之后异步地执行任务。如果你的目标框架是.NET Framework 4.0,需使用Task.ContinueWith方法:

public class CustomHeaderHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith(
(task) =>
{
HttpResponseMessage response = task.Result;
response.Headers.Add("X-Custom-Header", "This is my custom header.");
return response;
}
);
}
}


Example: Checking for an API Key

示例:检查API键

Some web services require clients to include an API key in their request. The following example shows how a message handler can check requests for a valid API key:

有些Web服务需要客户端在其请求中包含一个API键。以下示例演示一个消息处理器如何针对一个有效的API键来检查请求:

public class ApiKeyHandler : DelegatingHandler
{
public string Key { get; set; }

public ApiKeyHandler(string key)
{
this.Key = key;
}

protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (!ValidateKey(request))
{
var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response);
return tsc.Task;
}
return base.SendAsync(request, cancellationToken);
}

private bool ValidateKey(HttpRequestMessage message)
{
var query = message.RequestUri.ParseQueryString();
string key = query["key"];
return (key == Key);
}
}


This handler looks for the API key in the URI query string. (For this example, we assume that the key is a static string. A real implementation would probably use more complex validation.) If the query string contains the key,
the handler passes the request to the inner handler.

该处理器在URI查询字符串中查找API键(对此例而言,我们假设键是一个静态字符串。一个真实的实现可能会使用更复杂的验证。)如果查询字符串包含这个键,该处理便把请求传递给内部处理器。

If the request does not have a valid key, the handler creates a response message with status 403, Forbidden. In this case, the handler does not call
base.SendAsync, so the inner handler never receives the request, nor does the controller. Therefore, the controller can assume that all incoming requests have a valid API key.

如果请求没有一个有效的键,该处理器便创建一个状态为“403 — 拒绝访问”的响应消息。在这种情况下,该处理器不会调用base.SendAsync,于是,内部处理不会收到这个请求,控制器也就不会收到这个请求了。因此,控制器可以假设所有输入请求都有一个有效的API键。

If the API key applies only to certain controller actions, consider using an action filter instead of a message handler. Action filters run after URI routing is performed.

如果API键仅运用于某些控制器动作,请考虑使用动作过滤器,而不是消息处理器。动作过滤在URI路由完成之后运行。

Per-Route Message Handlers

单路由消息处理器

Handlers in the HttpConfiguration.MessageHandlers collection apply globally.

HttpConfiguration.MessageHandlers集合中的处理器是全局运用的。

Alternatively, you can add a message handler to a specific route when you define the route:

另一种办法是,在定义路由时,对一个特定的路由添加消息处理器:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Route1",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
name: "Route2",
routeTemplate: "api2/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
            handler: new MessageHandler2()  // per-route message handler(单路由消息处理器)
);

        config.MessageHandlers.Add(new MessageHandler1());  // global message handler(全局消息处理器)
}
}


In this example, if the request URI matches "Route2", the request is dispatched to
MessageHandler2. The following diagram shows the pipeline for these two routes:

在这个例子中,如果请求的URI与“Route2”匹配,该请求被分发给MessageHandler2。图5-4展示了这两个路由的管线:



图5-4. 单路由消息处理器

Notice that MessageHandler2 replaces the default
HttpControllerDispatcher. In this example,
MessageHandler2
creates the response, and requests that match "Route2" never go to a controller. This lets you replace the entire Web API controller mechanism with your own custom endpoint.

注意,MessageHandler2替换了默认的HttpControllerDispatcher。在这个例子中,MessageHandler2创建响应,而与“Route2”匹配的请求不会到达控制器。这让你可以用自己的自定义端点来替换整个Web API控制器机制。

Alternatively, a per-route message handler can delegate to
HttpControllerDispatcher, which then dispatches to a controller.

另一种办法是,单路由消息处理器可以委托给HttpControllerDispatcher,然后由它来分发给控制器(如图5-5所示)。



图5-5. 将消息处理器委托给HttpControllerDispatcher

The following code shows how to configure this route:

以下代码演示如何配置这种路由:

// List of delegating handlers.
// 委托处理器列表
DelegatingHandler[] handlers = new DelegatingHandler[] {
new MessageHandler3()
};
// Create a message handler chain with an end-point.
// 创建带有端点的消息处理器链
var routeHandlers = HttpClientFactory.CreatePipeline(
new HttpControllerDispatcher(config), handlers);
config.Routes.MapHttpRoute(
name: "Route2",
routeTemplate: "api2/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: routeHandlers
);


[/b]



5.2 Sending HTML Form Data

5.2 发送HTML表单数据

本文引自:http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-1

By Mike Wasson|June 15, 2012

作者:Mike Wasson | 日期:2012-6-15

Part 1: Form-urlencoded Data

第1部分:URL编码的表单数据

This article shows how to post form-urlencoded data to a Web API controller.

本文显示如何向Web API控制器递交URL编码的表单数据。

Overview of HTML Forms

HTML表单概述
Sending Complex Types

发送复合类型
Sending Form Data via AJAX

通过AJAX发送表单数据
Sending Simple Types

发送简单类型

Download the completed project.

下载完整的项目。

Overview of HTML Forms

HTML表单概述

HTML forms use either GET or POST to send data to the server. The method attribute of the
form element gives the HTTP method:

HTML表单使用GET或POST将数据发送给服务器。form元素的method标签属性给出HTTP方法:

<form action="api/values" method="post">


The default method is GET. If the form uses GET, the form data is encoded in the URI as a query string. If the form uses POST, the form data is placed in the request body. For POSTed data, the
enctype attribute specifies the format of the request body:

默认方法是GET。如果form使用GET,表单数据作为查询字符串被编码到URI中。如果form使用POST,表单数据被放在请求体中。对于POST的数据,enctype标签属性会指明请求体的格式:

enctypeDescription

描述
application/x-www-form-urlencodedForm data is encoded as name/value pairs, similar to a URI query string. This is the default format for POST.

表单数据被编码成“名字/值”对,类似于URI查询字符串。这是POST的默认格式。
multipart/form-dataForm data is encoded as a multipart MIME message. Use this format if you are uploading a file to the server.

表单数据被编码成多部分MIME消息。如果把文件上传到服务器,使用的是这种格式。
MIME指Multipurpose Internet Mail Extensions — 多用途互联网邮件扩展,它是通过网络传递邮件消息的一个互联网标准。MIME规定了用于表示各种数据类型的符号化方法。在HTTP协议中,对HTTP消息的内容类型也采用了MIME的这种表示数据类型的方法。上述enctype标签属性意为“编码类型”,就是用来指定HTTP消息的Content-Type(内容类型)报头属性。给这个标签属性所指定的值必须是MIME对Content-Type所规定的值之一。上表中便是MIME中关于内容类型的其中两个值。更多内容请参阅MIME的有关资料
— 译者注

Part 1 of this article looks at x-www-form-urlencoded format. Part 2 describes multipart MIME.

本文的第1部分考察x-www-form-urlencoded格式。第2部分描述多部分MIME。

Sending Complex Types

发送复合类型

Typically, you will send a complex type, composed of values taken from several form controls. Consider the following model that represents a status update:

典型地,你要发送的是一个复合类型,它由几个表单控件的值所组成。考虑以下表示状态更新的一个模型:

namespace FormEncode.Models
{
using System;
using System.ComponentModel.DataAnnotations;

public class Update
{
[Required]
[MaxLength(140)]
public string Status { get; set; }

public DateTime Date { get; set; }
}
}


Here is a Web API controller that accepts an Update object via POST.

以下是通过POST接收Update对象的一个Web API控制器。

namespace FormEncode.Controllers
{
using FormEncode.Models;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;

public class UpdatesController : ApiController
{
static readonly Dictionary<Guid, Update> updates = new Dictionary<Guid, Update>();

[HttpPost]
[ActionName("Complex")]
public HttpResponseMessage PostComplex(Update update)
{
if (ModelState.IsValid && update != null)
{
// Convert any HTML markup in the status text.
// 转换status文本中的HTML标记。
update.Status = HttpUtility.HtmlEncode(update.Status);

// Assign a new ID.
// 赋一个新的ID。
var id = Guid.NewGuid();
updates[id] = update;

// Create a 201 response.
// 创建一个201响应。
var response = new HttpResponseMessage(HttpStatusCode.Created)
{
Content = new StringContent(update.Status)
};
response.Headers.Location =
new Uri(Url.Link("DefaultApi", new { action = "status", id = id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}

[HttpGet]
public Update Status(Guid id)
{
Update update;
if (updates.TryGetValue(id, out update))
{
return update;
}
else
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
}
}


This controller uses
action-based routing, so the route template is "api/{controller}/{action}/{id}". The client will post the data to "/api/updates/complex".

这个控制器使用了“基于动作的路由(本系列教程的第4.1小节 — 译者注)”,因此,路由模板是“api/{controller}/{action}/{id}”。客户端会把这些数据递交给“/api/updates/complex”。

Now let’s write an HTML form for users to submit a status update.

现在,让我们编写一个用户递交状态更新的HTML表单。

<h1>Complex Type</h1>
<form id="form1" method="post" action="api/updates/complex"
enctype="application/x-www-form-urlencoded"> 
<div>
<label for="status">Status</label>
</div>
<div>
<input name="status" type="text" />
</div>
<div>
<label for="date">Date</label>
</div>
<div>
<input name="date" type="text" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>


Notice that the action attribute on the form is the URI of our controller action. Here is the form with some values entered in:

注意,form的action标签属性是控制器动作的URI。以下是已经输入了一些值的表单:



图5-6. 输入了一些数据的表单

When the user clicks Submit, the browser sends an HTTP request similar to the following:

当用户点击“Submit”时,浏览器发送一个类似于以下数据的HTML请求:

POST http://localhost:38899/api/updates/complex HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Content-Type: application/x-www-form-urlencoded
Content-Length: 47

status=Shopping+at+the+mall.&date=6%2F15%2F2012


Notice that the request body contains the form data, formatted as name/value pairs. Web API automatically converts the name/value pairs into an instance of the Update class.

注意,请求体包含了表单数据,被格式化成“名字/值”对。Web API会自动地把“名字/值”对转换成Update类的实例。

Sending Form Data via AJAX

通过AJAX发送表单数据

When a user submits a form, the browser navigates away from the current page and renders the body of the response message. That’s OK when the response is an HTML page. With a web API, however, the response body is usually either empty or contains structured
data, such as JSON. In that case, it makes more sense to send the form data using an AJAX request, so that the page can process the response.

当用户递交表单时,浏览器会离开当前页面,并渲染响应消息体。当响应是HTML页面时,这没问题。然而,对于Web API,响应体通常是空的,或是如JSON那样的结构化数据。在这种情况下,用AJAX请求发送表单数据,以使页面能够处理响应,会更有意义些。

The following code shows how to post form data using jQuery.

以下代码演示如何用jQuery递交表单数据。

<script type="text/javascript">
$("#form1").submit(function () {
var jqxhr = $.post('api/updates/complex', $('#form1').serialize())
.success(function () {
var loc = jqxhr.getResponseHeader('Location');
var a = $('<a/>', { href: loc, text: loc });
$('#message').html(a);
})
.error(function () {
$('#message').html("Error posting the update.");
});
return false;
});
</script>


The jQuery submit function replaces the form action with a new function. This overrides the default behavior of the Submit button. The
serialize function serializes the form data into name/value pairs. To send the form data to the server, call
$.post().

jQuery的submit函数用一个新函数替换了表单的action。它重写了Submit按钮的默认行为。serialize函数把表单数据序列化成“名字/值”对。为了将表单数据发送给服务器,调用$.post()

When the request completes, the .success() or
.error() handler displays an appropriate message to the user.

当请求完成时,.success().error()处理器会给用户显示一条相应的消息(见图5-7)。



图5-7. 通过AJAX发送表单数据

Sending Simple Types

发送简单类型

In the previous sections, we sent a complex type, which Web API deserialized to an instance of a model class. You can also send simple types, such as a string.

在前一小节中,我们发送的是复合类型,Web API会将其解序列化成一个模型类实例。你也可以发送简单类型,如字符串。

Before sending a simple type, consider wrapping the value in a complex type instead. This gives you the benefits of model validation on the server side, and makes it easier to extend your model if needed.

在发送简单类型之前,请考虑将值封装成复合类型。其好处是你可以在服务器端进行模型验证,并在必要时扩展模型。

The basic steps to send a simple type are the same, but there are two subtle differences. First, in the controller, you must decorate the parameter name with the
FromBody attribute.

发送简单类型的基本步骤是相同的,但有两个细微的差别。第一,在控制器中,你必须用FromBody注解属性来修饰参数名。

[HttpPost]
[ActionName("Simple")]
public HttpResponseMessage PostSimple([FromBody] string value)
{
if (value != null)
{
Update update = new Update()
{
Status = HttpUtility.HtmlEncode(value),
Date = DateTime.UtcNow
};

var id = Guid.NewGuid();
updates[id] = update;

var response = new HttpResponseMessage(HttpStatusCode.Created)
{
Content = new StringContent(update.Status)
};
response.Headers.Location =
new Uri(Url.Link("DefaultApi", new { action = "status", id = id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}


By default, Web API tries to get simple types from the request URI. The
FromBody
attribute tells Web API to read the value from the request body.

默认地,Web API试图通过请求的URI获取简单类型。FromBody注解属性告诉Web API从请求体读取这个值。

Web API reads the response body at most once, so only one parameter of an action can come from the request body. If you need to get multiple values from the request body, define a complex type.

Web API最多读取响应体一次,因此只有动作的一个参数可以获自请求体。如果需要从请求体得到多个值,需要定义复合类型。

Second, the client needs to send the value with the following format:

第二,客户端需要用以下格式发送这个值:

=value


Specifically, the name portion of the name/value pair must be empty for a simple type. Not all browsers support this for HTML forms, but you create this format in script as follows:

特别地,“名字/值”对的值部分对于简单类型必须为空。并不是所有浏览器对HTML表单都支持这种格式,但你在脚本中按下列方式创建了格式:

$.post('api/updates/simple', { "": $('#status1').val() });


Here is an example form:

以下是一个示例表单:

<h1>Simple Type</h1>
<form id="form2">
<div>
<label for="status">Status</label>
</div>
<div>
<input id="status1" type="text" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>


And here is the script to submit the form value. The only difference from the previous script is the argument passed into the
post function.

以下是递交表单值的脚本。与前面的脚本唯一的差别是在post函数中传递的参数。

$('#form2').submit(function () {
var jqxhr = $.post('api/updates/simple', { "": $('#status1').val() })
.success(function () {
var loc = jqxhr.getResponseHeader('Location');
var a = $('<a/>', { href: loc, text: loc });
$('#message').html(a);
})
.error(function () {
$('#message').html("Error posting the update.");
});
return false;
});


You can use the same approach to send an array of simple types:

可以用同样的办法发送简单类型的数组:

$.post('api/updates/postlist', { "": ["update one", "update two", "update three"] });

5.3 Sending HTML Form Data 5.3 发送HTML表单数据(2)

本文引自:http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2 By Mike Wasson|June 21, 2012 作者:Mike Wasson | 日期:2012-6-21

Part 2: File Upload and Multipart MIME 第2部分:文件上传与多部分MIME

This tutorial shows how to upload files to a web API. It also describes how to process multipart MIME data. 本教程演示如何对Web API上传文件。也描述如何处理多部分MIME数据。 Download the completed project. 下载完整的项目。 Here is an example of an HTML form for uploading a file: 以下是一个上传文件的HTML表单(如图5-8): [code]<form name="form1" method="post" enctype="multipart/form-data" action="api/upload"> <div> <label for="caption">Image Caption</label> <input name="caption" type="text" /> </div> <div> <label for="image1">Image File</label> <input name="image1" type="file" /> </div> <div> <input type="submit" value="Submit" /> </div> </form>


图5-8. 文件上传表单
This form contains a text input control and a file input control. When a form contains a file input control, the enctype attribute should always be "multipart/form-data", which specifies that the form will be sent as a multipart MIME message.
该表单有一个文本输入控件和一个文件输入控件。当表单含有文件输入控件时,其enctype标签属性应当总是“multipart/form-data”,它指示此表单将作为多部分MIME消息进行发送。
The format of a multipart MIME message is easiest to understand by looking at an example request:
通过考察一个示例请求,很容易理解multipart(多部分)MIME消息的格式:
POST http://localhost:50460/api/values/1 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------41184676334
Content-Length: 29278

-----------------------------41184676334
Content-Disposition: form-data; name="caption"

Summer vacation
-----------------------------41184676334
Content-Disposition: form-data; name="image1"; filename="GrandCanyon.jpg"
Content-Type: image/jpeg

(Binary data not shown)(二进制数据,未列出)
-----------------------------41184676334--
This message is divided into two parts, one for each form control. Part boundaries are indicated by the lines that start with dashes.
这条消息分成两个部件(parts),分别用于每个表单控件。部件边界由以一些破折号开始的行指示。
The part boundary includes a random component ("41184676334") to ensure that the boundary string does not accidentally appear inside a message part.
部件边界包含了一个随机组件(“41184676334”),以确保边界字符串不会偶然出现在消息部件之中。
Each message part contains one or more headers, followed by the part contents.
每一个消息部件含有一个或多个报头,后跟部件内容。
The Content-Disposition header includes the name of the control. For files, it also contains the file name.
Content-Disposition(内容布置)报头包括控件名称。对于文件,它也包括文件名。
The Content-Type header describes the data in the part. If this header is omitted, the default is text/plain.
Content-Type(内容类型)报头描述部件中的数据。如果该报头被忽略,默认为text/plain(文本格式)。
In the previous example, the user uploaded a file named GrandCanyon.jpg, with content type image/jpeg; and the value of the text input was "Summer Vacation".
在上一示例中,用户上传名为GrandCanyon.jpg的文件,其内容类型为image/jpeg,而文本输入框的值为“Summer Vacation”。

File Upload
文件上传

Now let's look at a Web API controller that reads files from a multipart MIME message. The controller will read the files asynchronously. Web API supports asynchronous actions using the task-based programming model. First, here is the code if you are targeting .NET Framework 4.5, which supports the async and await keywords.
现在,让我们考查从一个多部分MIME消息读取文件的控制器。该控制器将异步读取文件。Web API支持使用“基于任务的编程模型(这是关于.NET并行编程的MSDN文档 — 译者注)”的异步动作。首先,以下是针对.NET Framework 4.5的代码,它支持asyncawait关键字。
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
// 检查该请求是否含有multipart/form-data
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}

string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);

try
{
// Read the form data.
// 读取表单数据
await Request.Content.ReadAsMultipartAsync(provider);

// This illustrates how to get the file names.
// 以下描述如何获取文件名
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}
Notice that the controller action does not take any parameters. That's because we process the request body inside the action, without invoking a media-type formatter.
注意,控制器动作不取任何参数。这是因为我们是在动作中处理请求体的,不必调用media-type格式化器。
The IsMultipartContent method checks whether the request contains a multipart MIME message. If not, the controller returns HTTP status code 415 (Unsupported Media Type).
IsMultipartContent方法检查该请求是否含有多部分MIME消息。如果不是,控制器返回HTTP状态码415(不支持的媒体类型)。
The MultipartFormDataStreamProvider class is a helper object that allocates file streams for uploaded files. To read the multipart MIME message, call the ReadAsMultipartAsync method. This method extracts all of the message parts and writes them into the streams provided by the MultipartFormDataStreamProvider.
MultipartFormDataStreamProvider类是一个辅助器对象,它为上传文件分配文件流。为了读取多部分MIME消息,需调用ReadAsMultipartAsync方法。该方法提取所有消息部件,并把它们写入由MultipartFormDataStreamProvider提供的流中。
When the method completes, you can get information about the files from the FileData property, which is a collection of MultipartFileData objects.
当该方法完成时,你可以通过FileData属性获得文件的信息,该属性是一个MultipartFileData对象的集合。
MultipartFileData.FileName is the local file name on the server, where the file was saved.
MultipartFileData.FileName是保存此文件的服务器上的一个本地文件名。
MultipartFileData.Headers contains the part header (not the request header). You can use this to access the Content_Disposition and Content-Type headers.
MultipartFileData.Headers含有部件报头(不是请求报头)。你可以用它来访问Content_Disposition和Content-Type报头。
As the name suggests, ReadAsMultipartAsync is an asynchronous method. To perform work after the method completes, use a continuation task (.NET 4.0) or the await keyword (.NET 4.5).
正如名称所暗示的那样,ReadAsMultipartAsync是一个异步方法。为了在该方法完成之后执行一些工作,需要使用“continuation(继续)任务”(.NET 4.0)或await关键字(.NET 4.5)。
Here is the .NET Framework 4.0 version of the previous code:
以下是前述代码的.NET Framework 4.0版本:
public Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
// 检查该请求是否含有multipart/form-data
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}

string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);

// Read the form data and return an async task.
// 读取表单数据,并返回一个async任务
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}

// This illustrates how to get the file names.
// 以下描述了如何获取文件名
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
});

return task;
}

Reading Form Control Data
读取表单控件数据

The HTML form that I showed earlier had a text input control.
前面显示的HTML表单有一个文本输入控件。
<div>
<label for="caption">Image Caption</label>
<input name="caption" type="text" />
</div>
You can get the value of the control from the FormData property of the MultipartFormDataStreamProvider.
可以通过MultipartFormDataStreamProviderFormData属性获取控件的值。
public async Task<HttpResponseMessage> PostFormData()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}

string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);

try
{
await Request.Content.ReadAsMultipartAsync(provider);

// Show all the key-value pairs.
// 显示所有“键-值”对
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
Trace.WriteLine(string.Format("{0}: {1}", key, val));
}
}

return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
FormData is a NameValueCollection that contains name/value pairs for the form controls. The collection can contain duplicate keys. Consider this form:
FormData是一个NameValueCollection(名字/值集合),它含有表单控件的“名字/值”对。该集合可能含有重复键。考虑以下表单(如图5-9):
<form name="trip_search" method="post" enctype="multipart/form-data" action="api/upload">
<div>
<input type="radio" name="trip" value="round-trip"/>
Round-Trip
</div>
<div>
<input type="radio" name="trip" value="one-way"/>
One-Way
</div>

<div>
<input type="checkbox" name="options" value="nonstop" />
Only show non-stop flights
</div>
<div>
<input type="checkbox" name="options" value="airports" />
Compare nearby airports
</div>
<div>
<input type="checkbox" name="options" value="dates" />
My travel dates are flexible
</div>

<div>
<label for="seat">Seating Preference</label>
<select name="seat">
<option value="aisle">Aisle</option>
<option value="window">Window</option>
<option value="center">Center</option>
<option value="none">No Preference</option>
</select>
</div>
</form>


图5-9. 有重复键的表单
The request body might look like this:
该请求体看上去可能像这样:
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="trip"

round-trip
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="options"

nonstop
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="options"

dates
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="seat"

window
-----------------------------7dc1d13623304d6--
In that case, the FormData collection would contain the following key/value pairs:
在这种情况下,FormData集合会含有以下“键/值”对:
trip: round-trip
options: nonstop
options: dates
seat: window

[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐