简体   繁体   English

使用JSON参数简化REST URL

[英]Simplifying REST URL with JSON Parameters

I have been assigned a task and I really don't know how to even begin solving it, so any help would be appreciated. 我被分配了一项任务,我真的不知道该如何开始解决它,因此我们将不胜感激。 Consider the following example: 考虑以下示例:

@Path("/v1/{server}:{port}/instance")  
public class WSInstance {
        private static final Log        log = LogFactory.getLog(WSInstance.class);

        private final String    PLANNING_PROPNAME = "**PLNG_NAME**";
        private final String    PLANNING_PROPVAL = "**CALENDAR_NAME**";

        @GET
        @Path("/{instanceName}")
        @Produces("text/plain")
        public String getInstanceStatus(@Context HttpHeaders headers, 
                @PathParam("server")String server, 
                @PathParam("port")int port,
                @PathParam("instanceName") String instName,
                @DefaultValue("") @QueryParam("date") String date,
                @DefaultValue("") @QueryParam("instnum") String numexec)
        {
            return getInstanceStatus(Utils.extractUserInfo(headers), server, port, instName, numexec, date);    
        }

An example of a call to the aforementioned method is going to look like this: 调用上述方法的示例如下所示:

/v1/serverName:portNumber/instance/toto?date=21090207&instnum=0000

What the task is asking is to replace all the variables in that url ( serverName , portNumber , toto , date and instnum ) with json parameters. 任务要问的是用json参数替换该url中的所有变量( serverNameportNumbertotodateinstnum )。 This is meant to simplify the REST URL. 这是为了简化REST URL。

Any idea where to begin? 知道从哪里开始吗?


** EDIT : Thanks to everyone for their answers, you've certainly helped me a lot. ** 编辑 :感谢大家的回答,您当然对我有很大帮助。 Here's what I have done so far: 到目前为止,这是我所做的:

I decided to take a "simpler" class and method to familiarize myself with the procedure. 我决定采用“更简单”的类和方法来熟悉该过程。 So I took this one: 所以我拿了这个:

@Path("/v2/{server}:{port}/admin/")
public class WSAdmin {
    private static final Log log    = LogFactory.getLog(WSAdmin.class);

    @PUT
    @Path("/device")
    @Produces("text/plain")
    @Consumes("application/json")
    public String putDevice(String jsonObject, @Context HttpHeaders headers,
                                    @PathParam("server")String server,
                                    @PathParam("port")int port)
    {
        ObjectMapper mapper = new ObjectMapper();

        try
        {
            return updateDevice(mapper.readTree(jsonObject), Utils.extractUserInfo(headers), server, port);
        }
        catch (JsonProcessingException e)
        {
            return e.getMessage();
        }
        catch (IOException e)
        {
            return e.getMessage();
        }
    }

I changed it like this: 我这样更改它:

@Path("/v2/admin/")
public class WSAdmin {
    private static final Log log    = LogFactory.getLog(WSAdmin.class);

    @POST
    @Path("/device")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response putDevice(Admin admin)
    {
            String output = admin.toString();
            return Response.status(200).entity(output).build();
    }

Then I created the corresponding POJO: 然后,我创建了相应的POJO:

@XmlRootElement
public class Admin {

    private String server;
    private int port;
    private Date date;
    private String instnum;

// Constructors + getters + setters

    @Override
    public String toString() {
        return new StringBuffer("Server: ").append(this.server)
                .append("Port: ").append(this.port).append("Date: ")
                .append(this.date).append("InstNum: ")
                .append(this.instnum).toString();
    }
}

Then I edited the web.xml file to be able marshal and unmarshal Java Objets: 然后,我编辑了web.xml文件以能够封送和取消封送Java Objets:

<init-param>
    <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
    <param-value>true</param-value>
</init-param>

But for some reason, i'm getting the following error when I make the call from postman: 但是由于某种原因,我从邮递员打来电话时出现以下错误:

GRAVE [http-nio-8080-exec-5] com.sun.jersey.spi.container.ContainerRequest.getEntity A message body reader for Java class com.ws.v3.models.Admin, and Java type class com.ws.v3.models.Admin, and MIME media type application/json was not found.
The registered message body readers compatible with the MIME media type are:
application/json ->
  com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$App
  com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$App
  com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$App
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
  com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
  com.sun.jersey.core.impl.provider.entity.EntityHolderReader
  com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General

Those who had a similar error got it to disappear by adding either gerson or jersey-json in pom.xml. 那些有类似错误的人可以通过在pom.xml中添加gersonjersey-json来使其消失。 I've added them but the problem didn't get fixed. 我添加了它们,但问题没有得到解决。

Any idea? 任何想法?

Probably you have to change the method type to POST and pass the data as json in the body of the request. 可能您必须将方法类型更改为POST,并在请求正文中将数据作为json传递。

The GET request GET请求

GET /v1/yourServerName:8080/instance/toto?date=21090207&instnum=0000

can become the following POST request 可以成为以下POST请求

POST /v1/instance
{
  "serverName":"yourServerName",
  "portNumber":8080,
  "date":21090207,
  "instnum":"0000"
}

Note that instnum is not a numeric field because you passed the string 0000 that can't be represented as a numeric value. 请注意, instnum不是数字字段,因为您传递了不能表示为数字值的字符串0000。 Instead portNumber and date can be numeric values. 而是portNumberdate可以是数字值。

Consider using Jackson. 考虑使用Jackson。 Jackson maps JSON <-> objects. Jackson映射JSON <->对象。 Read about how you can use it with Jersey (REST) here: https://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/ 在此处了解如何在Jersey(REST)中使用它: https//www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/

Per specification the payload of a GET request is undefined. 根据规范GET请求的有效负载未定义。 You should, therfore, refrain from sending a body with a GET request. 因此,您应该避免发送带有GET请求的正文。 As Davide already suggested you should switch to POST here instead, as here the semantics of a payload received are defined by you, the server/API maintainer. 正如Davide所建议的那样,您应该在此处改用POST ,因为在这里,服务器/ API维护者由您定义了收到的有效负载的语义。

However, as you've tagged your post with you should probably consider copying the concepts used in the Web for over 2 decades now and translate it to your API design. 但是,当您将自己的帖子标记为您可能应该考虑复制Web上使用了20多年的概念,并将其转换为API设计。 First, REST architecture don't care about the structure of your URI. 首先,REST体系结构不关心URI的结构。 The URI itself is just a pointer to a resource and clients shouldn't need to interpret it nor hack it. URI本身只是指向资源的指针,客户端不需要解释它或修改它。 All the information needed for a client to make distinctive choices should be provided by the server to start with. 首先,服务器应提供客户做出独特选择所需的所有信息。 As clients shouldn't parse and interpret URIs how do they determine whether a URI is of use for the client or not? 由于客户端不应该解析和解释URI,因此如何确定客户端是否使用URI?

Well, how do we humans interact with URIs in web-pages? 那么,我们人类如何与网页中的URI交互? Usually they are annotated with human readable text that summarizes the content of that link (as with the specification ie above). 通常,它们用人类可读的文本进行注释,这些文本总结了该链接的内容(与上面的规范一样)。 Such short but meaningful names are usually called link-relation names and should be "attachted" to each URI. 这种简短但有意义的名称通常称为链接关系名称,应“附加”到每个URI。 A client reading such link-relation names and just invoke the accompanying URI will be able to continue its task if the server ever has the need to change its URI structure. 如果服务器需要更改其URI结构,则读取此类链接关系名称并仅调用随附URI的客户端将能够继续其任务。 This is one important step towards decoupling clients from servers. 这是将客户端与服务器分离的重要一步。 Such link relation names should be standardized but may also be specified in common knowlege or specified in media-types itself. 这样的链接关系名称应该是标准化的,但也可以在常识中指定,也可以在媒体类型本身中指定。

A common mistake many so called "REST APIs" do is to support either application/xml and/or application/json only. 许多所谓的“ REST API”的常见错误是仅支持application/xml和/或application/json Those are very poor media types in a REST architecture as they only define the syntax to use but not the semantics of the respective elements. 在REST架构中,那些媒体类型非常差,因为它们仅定义要使用的语法,而没有定义各个元素的语义。 It is thus hard for a client to determine the intent of such a document and to easy to fall into the typed resource trap and assume that a certain resource has a certain type. 因此,对于客户而言,很难确定此类文档的意图,并且很难落入类型化的资源陷阱中并假定某个资源具有某种类型。 If such a (non-standardized) representation changes, chances are high that a client will break to interoperate with that service/API further. 如果这种(非标准化的)表示方式发生变化,则客户很可能会中断以进一步与该服务/ API进行互操作。

Media types are more or less standardized processing-rules for a certain received payload that should help give the receiver some meaning of the content and what it might do with it. 媒体类型或多或少是某种接收到的有效负载的标准化处理规则,应有助于使接收者了解内容的含义以及内容的用途。 One probably well known media type is HTML which defines when certain elements are feasible and the constraint each element has. HTML可能是一种众所周知的媒体类型,它定义了某些元素何时可行以及每个元素具有的约束。 It also defines how certain elements are rendered and how it is backward compatible with former versions. 它还定义了如何渲染某些元素以及如何与旧版本向后兼容。 It is the defacto standard when it comes to support for links and link-relations. 当支持链接和链接关系时,它是事实上的标准。 While HAL , Collection+JSON , ... are steps into the right direction in terms of support of links and relation names, they are far from providing the same semantics as HTML does, though they should be preferable to plain JSON as they not only specify a syntax but also semantics of certain elements such as _links ie which help the client to differentiate links from content. 尽管HALCollection + JSON ,...在支持链接和关系名称方面朝着正确的方向迈进,但它们远没有提供与HTML相同的语义,尽管它们应比纯JSON更可取,因为它们不仅指定语法以及某些元素(例如_links语义,即帮助客户端区分链接与内容。

Media types are especially important in terms of content-type negotiation where a client requests a server to return a representation format a client understands. 就内容类型协商而言,媒体类型尤其重要,在该类型中,客户端请求服务器返回客户端理解的表示格式。 If the server is not able to produce such a representation it will inform the client with an expressive enough error code (406). 如果服务器不能产生这样的表示,它将向客户端通知一个足够有表现力的错误代码(406)。 If the server is not able to process the media type provided by the client (on a POST, PUT, PATCH, ... operation) it will also inform the client that it does not understand such a format (415). 如果服务器不能处理客户端提供的媒体类型(在POST,PUT,PATCH,...操作上),它还将通知客户端它不理解这种格式(415)。

A general advice on designing REST APIs would be to think of the API in terms of a Web server and also desing the whole interaction with it like that. 设计REST API的一般建议是从Web服务器的角度考虑API,并以此来设计与之的整个交互。 Ie if a client has to perform certian input it should not send just a playin JSON document with some random fields (specified in some external documentation) to the server, but the server should teach the client on how to send such a request to start with. 即,如果客户端必须执行证书输入,则不应仅将带有一些随机字段(在某些外部文档中指定)的playin JSON文档发送到服务器,而是服务器应教会客户端如何发送此类请求以开头。 Similar to Web forms where humans should enter text and stuff, REST APIs should return a media-type representing a formular that teaches a client what fields the server expects, which operation to use and the target to send the payload to. 类似于Web表单,人们应该在Web表单中输入文本和内容,REST API应该返回一种表示公式的媒体类型,该公式可以告诉客户端服务器期望的字段,使用的操作以及将有效负载发送到的目标。

In regards to the actual question, I'm not sure why your employee is so keen on removing parameters from the URI. 关于实际问题,我不确定为什么您的员工如此热衷于从URI中删除参数。 As mentioned in my first paragraph on sending a payload you'd need to switch to POST and therefore automatically loose the guaranteed safe and idempotent features of the GET operation, besides not being cacheable by default. 正如我在发送有效负载的第一段中提到的那样,除了默认情况下不可缓存之外,您还需要切换到POST ,因此自动idempotent GET操作保证的safeidempotent功能。

What you can do ie is allow users or your coworkers to preconfigure certain queries and create short/tiny URLs for these preconfigured URIs. 您可以做的就是允许用户或您的同事预先配置某些查询,并为这些预先配置的URI创建简短的URL。 Here you should provide a form-like media type to the client where it can select the options to choose from and enter further necessary data. 在这里,您应该为客户端提供一种类似于表单的媒体类型,在该媒体类型中,客户端可以选择要选择的选项并输入其他必要数据。 Once you received such a request you store such a preconfiguration and return a short/tiny URL for that preconfiguration in the Location header of the response. 收到这样的请求后,您将存储这样的预配置,并在响应的Location标头中返回该预配置的简短URL。 You should also add the preconfigured links in the regular response so that a client is able to invoke it if it didn't store it right after persistence. 您还应该在常规响应中添加预配置的链接,以便客户端在持久化后未立即存储时就能够调用它。 By doing so you still have the benefits of the GET operation while having the flexibility to add new or customize existing queries on the fly. 这样,您仍然具有GET操作的优势,同时可以灵活地动态添加新查询或自定义现有查询。 As mentioned before, a client won't be able to make much use of such links in a plain application/json representation. 如前所述,客户端将无法在纯application/json表示形式中大量使用此类链接。 If the client supports application/hal+json it might at least know what link-relation and links are and therefore be able to lookup and invoke the URI via its accompanying link relation name. 如果客户端支持application/hal+json则它可能至少知道什么是链接关系和链接,因此能够通过其附带的链接关系名称来查找和调用URI。 This basically gives you the freedom to later on change the URI structure if needed without affecting the client. 基本上,您可以根据需要自由地稍后更改URI结构,而不会影响客户端。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM