您的位置:首页 > 其它

RESTful服务最佳实践——(七)

2017-03-28 20:59 441 查看

返回表征

正如前面提到的,RESTful接口支持多种资源表征,包括JSON和XML,以及被封装的JSON和XML。建议JSON作为默认表征,不过服务端应该允许客户端指定其他表征。

对于客户端请求的表征格式,是否使用文件扩展名风格格式、query-string参数等来组成Accept 头,这是个问题。理想情况下,服务端可以支持所有的这些方法。但是,现在行业内更倾向于使用一种类似文件扩展名的格式说明。因此,建议至少服务端需要支持使用文件拓展名,例如“.json”,“.xml”及其被封装选项“.wjon”,“.wxml”。

使用这种技术时,在URI中指定表现形式可以提高可见性。例如,GET http://www.example.com/customers.xml 将返回XML格式的客户表征。同样,GET http://www.example.com/customers.json 将返回一个JSON格式的表征。即使是在基本的客户端(例如“curl”)使用,这也使得服务端更易操作,因此推荐使用这种方式。

此外,当格式说明没有包含在url时,服务端应该返回默认格式(可假定为JSON)。例如:

GET http://www.example.com/customers/12345

GET http://www.example.com/customers/12345.json

以上两者返回的客户资源12345都为JSON格式,这就是服务端的默认格式。

GET http://www.example.com/customers/12345.xml

如果服务端支持的话,以上请求返回给客户端XML格式的资源12345。如果该服务器不支持XML格式的资源,将返回一个HTTP 404的错误。

使用HTTP Accept头被广泛认为是一种更优雅的方式,并且符合HTTP规范和含义,可以达到表明客户端如何告知HTTP服务端它们支持哪些内容类型的目的。但是,服务器为了同时支持封装和未封装的响应,为了使用Accept头,必须实现自定义类型–因为这些格式没有标准的类型。这大大增加了客户端和服务端的复杂性。请参见RFC 2616的14.1节有关Accept头的详细信息(http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1)。支持文件扩展名风格格式说明是简单直接的,用最少的字符数就可以做到,并且易于支持脚本–无需利用HTTP头。

通常当我们提到REST服务,跟XML是毫不相关的。即使支持XML,但几乎没有人建议在REST中使用XML。XML标准和公约真的不适用。特别是它连命名空间都没有,更不该在RESTful服务体系中使用。就像搅混了水,它只会使事情变得更复杂。所以返回的XML格式数据更像是JSON,它简单易读,没有模式和命名空间的限制,换句话来说是无标准,但易于理解。

通过链接的资源可发现性(HATEOAS续)

一个REST的指导原则(根据“统一接口”原则)是:应用状态通过超文本传输。正如上述“什么是Rest?”章节提到的,这通常被称为Hypertext As The Engine of Application State (HATEOAS,超文本表示应用程序状态)。

根据Roy Fielding的博客(http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertextdriven),REST接口中最重要的部分是超文本的作用。此外,他还指出,在给出一个API没有预先知识和外带信息的初始URI时,应该是可用的、可理解的。也就是说,一个API应当通过其链接,连接到数据的各个组件。不建议只返回纯数据。

这种做法并没有经常被服务器行业领导者遵循,这反映了HATEOAS的作用在成熟度模型中更高。纵观很多服务体系,惯例是返回更多的数据和较少的(或没有)链接。这是违背Fielding的REST约定的。Fielding说:“信息的每一个寻址单元携带一个地址……查询结果被带有摘要信息的链接清单所替代,而不是数组对象表征。”

另一方面,简单地返回链接的集合会是通信量的一个主要影响因素。在实际情况中,根据所需的条件或使用情况,API接口的通信量要根据服务器响应中超文本链接所包含的“摘要”数量来平衡。

同时,充分利用HATEOAS会增加实现的复杂性,并对服务客户端产生明显的负担,相当于降低了客户端和服务器端的开发人员的生产力。因此,当务之急是要平衡超链接服务实践和现有可用资源的问题。

超链接最小集的做法在最大限度减少了客户端和服务器之间的耦合的同时,提高了服务端的可用性、可操纵性和可理解性。这些最小化建议是:通过POST创建资源并从GET请求返回集合,在分页情况下的做法建议将在下文提到。

最小化链接推荐

在创建用例中,新建资源的URI(链接)应该在Location响应头中返回,且响应主体是空的–或者只包含新建资源的ID。

对于从服务端返回的表征集合,每个表征应该在它的链接集合中,携带一个最小的“自身”链接属性。其他的链接可能存于一个单独的链接集合返回以方便分页,在需要时带有“第一页”、“上一页”、“下一页”、“最后一页”等信息。

参照下文链接格式部分的例子获取更多信息。

链接格式

参照整个链接方式标准,建议遵守类似Atom、AtomPub或Xlink的风格。JSON-LD也会有一些关联,但并没有被广泛的采用(如果它曾经算被采用过)。业内最普遍的方式是使用Atom链接风格,该风格具有“rel”元素和“href”元素,包含不需要任何授权或者查询串参数的资源的完整URI。“rel”元素包含标准值“替代”、“关联”、“自身”、“附件”、“途径”,还有分页链接的“第一页”、“上一页”、“下一页”,“最后一页”。在需要时添加它们来使用,并且在需要时可以自定义。

一些XML Atom格式的概念对JSON表示的链接来说是无用的。例如,“方法”属性对于一个RESTful资源来说是不需要的,因为对给定资源来说,在所有支持的HTTP方法(CRUD行为)中,资源的URI都是相同的¬–所以单独列出这些是没有必要的。

让我们举一些具体的例子来进一步证明这种说法是对的。以下例子是,调用如下创建新资源的请求后响应如何:

POST http://api.example.com/users

以下样例是响应头带有创建新资源的URI的位置头的集合:

HTTP/1.1 201 CREATED

Status: 201

Connection: close

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

Location: http://api.example.com/users/12346

主体为空或包含一个被封装的响应(见下文被封装响应)。

以下样例是来自不含分页表征集合的GET请求的JSON响应:

{
"data": [
{
"user_id": "42",
"name": "Bob",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/42"
}
]
},
{
"user_id": "22",
"name": "Frank",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/22"
}
]
},
{
"user_id": "125",
"name": "Sally",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/125"
}

13d00
]
}
]
}


注意,链接数组中包含关联到集合中每个项的“self”引用。这个数组可能潜在地包含其他关系,如孩子、父母等。

最后一个样例是来自含有分页表征集合的GET请求的JSON响应(我们将用每页3个项为例),我们看下集合的第三页:

{
"data": [
{
"user_id": "42",
"name": "Bob",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/42"
}
]
},
{
"user_id": "22",
"name": "Frank",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/22"
}
]
},
{
"user_id": "125",
"name": "Sally",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/125"
}
]
}
],
"links": [
{
"rel": "first",
"href": "http://api.example.com/users?offset=0&limit=3"
},
{
"rel": "last",
"href": "http://api.example.com/users?offset=55&limit=3"
},
{
"rel": "previous",
"href": "http://api.example.com/users?offset=3&limit=3"
},
{
"rel": "next",
"href": "http://api.example.com/users?offset=9&limit=3"
}
]
}


在这个例子中,响应中用于分页的链接集合的每个项具有指向“自身(self)”的链接。这里可能有关联到集合的额外连接,但与分页无关。简而言之,在集合中有两个地方包含链接。集合(数据对象中所要求的表征集合),包括链接集合;单独的对象和链接,包括适用于整个集合的链接,如分页相关的链接–他们当中的项至少会包含一个“self”关联。

对于通过POST创建资源的用例,需包含一个关联新建对象链接的Location头。

封装响应

服务器可以同时在响应中返回HTTP状态码和主体。在许多JavaScript框架,HTTP状态响应码没有返回给后端开发者,这往往导致客户端无法根据状态码确定行为。此外,虽然HTTP规范中有很多种响应码,但是往往有只有少数客户端关心这些–它们经常被归结为“成功”、“错误”或“失败”。因此,将响应内容、响应码封装在包含响应信息的表征中,是有益的。

OmniTI 实验室有一个这样的提议,称为JSEND响应。更多的信息请参照http://labs.omniti.com/labs/jsend。另外一个提案是由Douglas Crockford提出的,可以在这里阅读http://www.json.org/JSONRequest.html

这些提案在实践中并没有完全地覆盖所有的情况。基本上现在的最好的做法是依照以下属性封装常规(非JSONP)响应:

Code(状态码)– 包含HTTP响应状态码的整型

Status(状态)– 包含文本:“成功”、“失败”或“错误”。HTTP状态响应值在500-599之间为“失败”,状态码400-499之间为“错误”,其他为“成功”(例如:响应为1XX、2XX和3XX)。

Message(信息)– 仅用于存放“失败”和“错误”状态的错误消息。参照国际化(il8n)标准,它可能包含信息号或者代码,可以单独使用,也可以用分隔符隔开同时包含。

Data(数据)– 包含响应主体。在“失败”和“错误”的状态下,data包含了错误原因或异常名称。

一个封装后的成功的响应是类似这样:

{
"code": 200,
"status": "success",
"data": {
"lacksTOS": false,
"invalidCredentials": false,
"authToken": "4ee683baa2a3332c3c86026d"
}
}


一个封装后的错误的响应类似是这样:

{
"code": 401,
"status": "error",
"message": "token is invalid",
"data": "UnauthorizedException"
}


在XML中,这2个封装的响应对应如下:

response>
<code>200</code>
<status>success</status>
<data class="AuthenticationResult">
<lacksTOS>false</lacksTOS>
<invalidCredentials>false</invalidCredentials>
<authToken>1.0|idm|idm|4ee683baa2a3332c3c86026d</authToken>
</data>
</response>




<response>
<code>401</code>
<status>error</status>
<message>token is invalid</message>
<data class="string">UnauthorizedException</data>
</response>


处理跨域问题

我们都听说过关于浏览器同源策略或共源的需求。换句话说,浏览器只能请求当前显示的站点的资源。例如,如果站点当前正在显示www.Example1.com,该站点是不能对www.Example.com进行请求。显然这会影响站点访问服务器的方式。

目前有两个被广泛接受的支持跨域请求的方法:JSONP和跨域资源共享(CORS)。JSONP或“填充的JSON”是一种使用模式,它提供了一个从服务器请求不同域数据的方法。它通过服务器返回的任意JavaScript代码,而不返回JSON,从而起作用。这些响应是由JavaScript解析器而不是由JSON解析器来解析的。另一方面,CORS是web浏览器的技术规范,定义了web服务器如何使它的资源被不同域的网页访问。它被看做是JSONP的最新替代品,并且可以被所有当下浏览器所支持。因此,不建议使用JSONP。任何情况下,推荐选择CORS。

支持CORS

在服务端实现CORS很简单,只要在发送响应时附带HTTP头,例如:

Access-Control-Allow-Origin: *

如果数据是公共的,访问来源才可以设置为“*”。在大多数情况下,Access-Control-Allow-Origin头应该指定可以发起CORS请求的域。只有需要跨域访问的URL需要设置CORS头。

Access-Control-Allow-Origin: http://example.com:8080 http://foo.example.com

以上在Access-Control-Allow-Origin头中设置只允许受信任的域访问。

Access-Control-Allow-Credentials: true

只在需要时使用这个头,因为如果用户已经登录到应用程序,它会发送cookies/sessions。

这些头可以通过web服务器、代理配置,或者从服务器本身发送。不推荐在服务端实现,因为很不灵活。或者,使用以上第二种格式,在web服务器配置适当域的列表。更多关于CORS的内容可以参照这里:http://enable-cors.org/

支持JSONP

JSONP通过利用GET请求避开浏览器限制,实现所有服务的调用。原理是请求方在请求上增加一个字符串查询参数(例如:jsonp=”jsonp_callback”),其中“jsonp”参数的值是JavaScript函数名,该函数在有响应返回时将会被调用。

由于GET请求中没有包含请求体,JSONP在使用时有着严重的局限性,因此数据必须通过字符串查询参数来传递。同样的,为了支持PUT,POST和DELETE方法,HTTP方法必须也通过字符串查询参数来传递,类似_method=POST这种形式。像这样的HTTP方法传送方式是不推荐使用的,这会让服务处于安全风险之中。

JSONP通常在不支持CORS的老旧浏览器中使用,但如果要支持CORS,则会影响服务的构建过程。或者,JSONP也可以通过代理来使用。总的来说,当需要时,JSONP都会为CORS提供支持。

为了在服务端支持JSONP,在JSONP字符串查询参数传递时,响应必须要执行一些以下操作:

响应体必须封装成一个参数传递给jsonp参数中指定的JavaScript函数(例如:jsonp_callback(“”))。

始终返回HTTP状态码200(OK),并且将真实的状态作为JSON响应中的一部分返回。

另外,响应体中常常必须包含响应头。这使得JSONP回调方法确定基于响应体的响应处理,因为它本身无法得知响应头和状态值。

以下是一个错误响应的例子,该例子按上述推荐方法封装响应值(注意:HTTP的响应状态是200):

jsonp_callback(
"{
'code': '404',
'status': 'error',
'headers': [],
'message': 'resource XYZ not found',
'data': 'NotFoundException'
}"
)


成功的创建响应则是像这样(HTTP的响应状态仍是200):

jsonp_callback(
"{
'code': '201',
'status': 'error',
'headers': [
{
'Location': 'http://www.example.com/customers/12345'
}
],
'data': '12345'
}")


原文如下

Returning Representations

As mentioned earlier, it is desirable for a service to support multiple representations of resources, including JSON and XML, as well as wrapped JSON and XML. As the default representation, the recommendation is JSON, but services should allow clients to specify alternative representations.

For a client to request a representation format, there is a question around whether to use the Accept header a file-extension-style format specifier, query-string parameter, etc. Optimally, services would support all of those methods. However, industry is currently converging on using a format specifier, which looks more like a file extension. Therefore, the recommendation is that, at a minimum, services support the use of file extensions such as ‘.json’, ‘.xml’ and wrapped options, ‘.wjson’ and ‘.wxml’.

Using this technique, the representation format is specified in the URI, enhancing visibility. For example, GET http://www.example.com/customers.xml would return the list of customer representations in XML format. Likewise, GET http://www.example.com/customers.json would return a JSON representation. This makes the services simple to use from even the most basic client (such as ‘curl’) and is recommended.

Also, services should return the default representation format (presumably JSON) when a format specifier is not included on the url. For example:

GET http://www.example.com/customers/12345

GET http://www.example.com/customers/12345.json

Both of the above return the 12345 customer resource in a JSON representation, which is the default format for this service.

GET http://www.example.com/customers/12345.xml

Returns the 12345 customer resource in an XML representation, if supported. If an XML representation of this resource is not supported by this service, an HTTP 404 error should be returned.

Use of the HTTP Accept header is considered by many to be a more elegant approach, and is in keeping with the meaning and intent of the HTTP specification with regards to how clients notify HTTP servers of which content types they support. However, to support both wrapped and unwrapped responses from your services, in order to utilize the Accept header, you must implement your own custom types—since there are no standard types for these formats. This increases the complexity of both clients and services greatly. See Section 14.1 of RFC 2616 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1) for details on the Accept header. Supporting file-extension-style format specifiers is simple, straight-forward, gets the job done in the fewest number of characters, and easily supports scripting—without having to leverage HTTP headers.

In general, when we talk about REST services, XML is largely irrelevant. Barely anyone uses XML with REST although supporting XML is recommended. XML standards and conventions are really not in play. In particular, namespaces are not, nor should they be use in a RESTful service context. It just muddies the waters and makes things more complicated. So the XML that is returned is more JSON like—simple and easy to read, without the schema and namespace constraints—non-standard in other words, but parse-able.

Resource Discoverability Through Links (HATEOAS cont’d)

One of the guiding principals of REST (via the Uniform Interface constraint) is that application state is communicated via hypertext. This is often referred to as Hypertext As The Engine of Application State (HATEOAS) as mentioned above in the What is Rest? Section.

According to Roy Fielding’s blog (at http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertextdriven), the most important part of a REST interface is its usage of hypertext. Further, he states that an API should be usable and understandable given an initial URI without prior knowledge or out-of-band information. That is, an API should be navigable via its links to various components of the data. Returning only data representations is discouraged.

This practice is not often followed by current industry leaders in services, reflecting that HATEOAS usage is higher on the maturity model. Looking around at many services, convention is to return more data and less (or no) links. This is contrary to Fielding’s REST constraints. Fielding says, “Every addressable unit of information carries an address… Query results are represented by a list of links with summary information, not by arrays of object representations.”

On the other hand, simply returning collections of links can be a major cause of network chattiness. In the real world, depending on requirements or use cases, chattiness of the API interface is managed by balancing how much “summary” data is included along with the relational hypertext links in service responses.

Also, full use of HATEOAS can increase implementation complexity and impose a significant burden on service clients, decreasing developer productivity on both client and server ends of the equation. Consequently, it is imperative to balance hyperlinking service implementations with available development resources.

A minimal set of hyperlinking practices provides major gains in service usability, navigability and understandability while minimizing development impact and reducing the coupling between client and server. These minimal recommendations are resources created via POST and for collections returned from GET requests, with additional recommendations for pagination cases, which are described below.

Minimal Linking Recommendations

In create use cases, the URI (link) for the newly-created resource should be returned in the Location response header and the response body be empty—or contain only the ID of the newly-created resource.

For collections of representations being returned from a service, each representation should minimally carry a ‘self’ link property in its own links collection. Other links may be present in the returned as a separate links collection to facilitate pagination, with ‘first’, ‘previous’, ‘next’, ‘last’ links where applicable.

See the examples in the Link Format section below for more information.

Link Format

Regarding overall link format standards it is recommended to adhere to some semblance of the Atom, AtomPub, or Xlink style. JSON-LD is getting some traction too, but is not widely adopted yet (if it ever will be). Most widespread in the industry is usage of the Atom link style with a “rel” element and an “href” element that contains the full URI for the resource without any authentication or query-string parameters. The “rel” element, can contain the standard values “alternate”, “related”, “self”, “enclosure”, and “via”, plus “first”, “last”, “previous”, “next” for pagination links. Use them where they make sense and add your own when needed.

Some of the XML Atom format concepts are somewhat irrelevant for links being represented in JSON. For instance, the METHOD property is not needed for a RESTful resource since the URIs are the same for a given resource, with all of the HTTP methods being supported (for CRUD behavior)–so listing them individually is overkill.

Let’s make all this talk a little more concrete with some examples. Here’s what the response would look like after creating a new resource with a call to:

POST http://api.example.com/users

And here’s an example set of response headers with the Location header set containing the new resource URI:

HTTP/1.1 201 CREATED

Status: 201

Connection: close

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

Location: http://api.example.com/users/12346

The body is either empty, or contains a wrapped response (see Wrapped Responses below).

Here is an example JSON response to a GET request that returns a collection of representations without pagination involved:

{
"data": [
{
"user_id": "42",
"name": "Bob",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/42"
}
]
},
{
"user_id": "22",
"name": "Frank",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/22"
}
]
},
{
"user_id": "125",
"name": "Sally",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/125"
}
]
}
]
}


Note the links array containing a single reference to “self” for each item in the collection. This array could potentially contain other relationships, such as children, parent, etc.

The final example is a JSON response to a GET request that returns a collection where pagination is involved (we’re using three items per page) and we’re on the third page of the collection:

{
"data": [
{
"user_id": "42",
"name": "Bob",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/42"
}
]
},
{
"user_id": "22",
"name": "Frank",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/22"
}
]
},
{
"user_id": "125",
"name": "Sally",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/125"
}
]
}
],
"links": [
{
"rel": "first",
"href": "http://api.example.com/users?offset=0&limit=3"
},
{
"rel": "last",
"href": "http://api.example.com/users?offset=55&limit=3"
},
{
"rel": "previous",
"href": "http://api.example.com/users?offset=3&limit=3"
},
{
"rel": "next",
"href": "http://api.example.com/users?offset=9&limit=3"
}
]
}


In this example, the links collection in the response is populated for pagination purposes along with the link to “self” in each of the items in the collection. There could be additional links here related to the collection but not related to pagination. The simple summary is, there are two places to include links in a collection. For each item in the collection (those in the data object, which is the collection of representations requested), include a links collection that, minimally, would contain a “self” reference. Then, in a separate object, links, include links that apply to the entire collection as applicable, such as pagination-related links.

For the create use case—create via POST, include a Location header with a link to the newly-created object.

Wrapped Responses

Services have the opportunity to return both HTTP status codes along with a body in the response. In many JavaScript frameworks, HTTP status response codes are not returned to the end-developer, often preventing the client from determining behavior based on that status code. Additionally, with the myriad response codes in the HTTP spec, often there are only a few that clients care about—frequently boiling down to ‘success’, ‘error’, or ‘failure’. Consequently, it is beneficial to wrap responses in a representation that contains information about the response as well as the response itself.

One such proposal is that from OmniTI Labs, the so-called JSEND response. More information can be found at http://labs.omniti.com/labs/jsend. Another option is proposed by Douglas Crockford and can be read about at http://www.json.org/JSONRequest.html.

In practice neither of these proposals adequately covers all cases. Basically, current best practice is to wrap regular (non-JSONP) responses with the following properties:

code – contains the HTTP response status code as an integer.

status – contains the text: “success”, “fail”, or “error”. Where “fail” is for HTTP status response values from 500-599, “error” is for statuses 400-499, and “success” is for everything else (e.g. 1XX, 2XX and 3XX responses).

message – only used for “fail” and “error” statuses to contain the error message. For internationalization (i18n) purposes, this could contain a message number or code, either alone or contained within delimiters.

data – that contains the response body. In the case of “error” or “fail” statuses, this contains the cause, or exception name.

A successful response in wrapped style looks similar to this:

{
"code": 200,
"status": "success",
"data": {
"lacksTOS": false,
"invalidCredentials": false,
"authToken": "4ee683baa2a3332c3c86026d"
}
}


An example error response in wrapped style looks like this:

{
"code": 401,
"status": "error",
"message": "token is invalid",
"data": "UnauthorizedException"
}


In XML, these two wrapped responses would correspond to:

response>
<code>200</code>
<status>success</status>
<data class="AuthenticationResult">
<lacksTOS>false</lacksTOS>
<invalidCredentials>false</invalidCredentials>
<authToken>1.0|idm|idm|4ee683baa2a3332c3c86026d</authToken>
</data>
</response>


And:

<response>
<code>401</code>
<status>error</status>
<message>token is invalid</message>
<data class="string">UnauthorizedException</data>
</response>


Handling Cross-Domain Issues

We’ve all heard about working around the browser’s same origin policy or common-source requirement. In other words, the browser can only make requests to the site it’s currently displaying. For example, if the site currently being displayed is www.Example1.com, then that site cannot perform a request against www.Example2.com. Obviously, this impacts how sites access services.

Presently, there are two widely-accepted methods to support cross-domain requests: JSONP and CrossOrigin Resource Sharing (CORS). JSONP or “JSON with padding” is a usage pattern that provides a method to request data from a server in a different domain. It works by the service returning arbitrary JavaScript code instead of JSON. These responses are evaluated by the JavaScript interpreter, not parsed by a JSON parser. CORS, on the other hand, is a web browser technology specification, which defines ways for a web server to allow its resources to be accessed by a web page from a different domain. It is seen as a modern alternative to JSONP and is supported by all modern browsers. Therefore, JSONP is not recommended. Choose CORS whenever and wherever possible.

Supporting CORS

Implementing CORS on a server is as simple as sending an additional HTTP header in the response, for example:

Access-Control-Allow-Origin: *

An access origin of ‘*’ should only be set if the data is meant for public consumption. In most cases the Access-Control-Allow-Origin header should specify which domains should be able to initiate a CORS request. Only URLs that need to be accessed cross-domain should have the CORS header set.

Access-Control-Allow-Origin: http://example.com:8080 http://foo.example.com

Allow only trusted domains in Access-Control-Allow-Origin header.

Access-Control-Allow-Credentials: true

Use this header only when necessary as it will send the cookies/sessions if the user is logged into the application.

These headers can be configured via the Web server, proxy or sent from the service itself. Implementing it within the services is not recommended as it’s not flexible. Instead, use the second form, a space delimited list of appropriate domains configured on your Web server. More about CORS can be found at: http://enable-cors.org/.

Supporting JSONP

JSONP gets around the browser limitation by utilizing GET requests to perform all service calls. In essence, the requester adds a query-string parameter (e.g. jsonp=”jsonp_callback”) to the request, where the value of the “jsonp” parameter is the name of a javascript function that will be called when the response is returned.

There severe limitations to the functionality enabled by JSONP, since GET requests do not contain a request body and, therefore, information must be passed via query-string parameters. Also, to support PUT, POST and DELETE operations, the effective HTTP method must also be passed as a query-string argument, such as _method=POST. Tunneling the HTTP method like this is not recommended and can open services up to security risks.

JSONP works on legacy browsers which preclude CORS support, but affects how services are built if they’re going to support it. Alternatively, JSONP can be implemented via a proxy. Overall, JSONP is being de-emphasized in favor of CORS. Favor CORS whenever possible.

To support JSONP on the server side, when the JSONP query-string parameter is passed in, the response must be manipulated a bit as follows:

The response body must be wrapped as the parameter to the given javascript function in the jsonp parameter (e.g. jsonp_callback(“”)).

Always return HTTP status 200 (OK) and return the actual status as part of the JSON response.

Additionally, it’s also often necessary to include headers as part of the response body. This enables the JSONP callback method to make decisions on response handling based on the response body since it’s not privy to the information in response headers and status.

An example error response following the above wrapped response recommendations is as follows (note: HTTP response status is 200):

jsonp_callback(
"{
'code': '404',
'status': 'error',
'headers': [],
'message': 'resource XYZ not found',
'data': 'NotFoundException'
}"
)


A successful creation response looks like this (still with an HTTP response status of 200):

jsonp_callback(
"{
'code': '201',
'status': 'error',
'headers': [
{
'Location': 'http://www.example.com/customers/12345'
}
],
'data': '12345'
}")
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: