Java for Web学习笔记(八三):RESTful(3)Controller

本文介绍RESTful API设计中的关键概念,包括异常处理、资源的增删改查操作及发现机制实现。提供了具体示例代码,展示了如何使用ResponseEntity进行响应定制、@RequestBody解析请求体、@ResponseBody直接返回对象等技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

controller的例子

完成异常处理后,我们可以看看RESTful controller的小例子,例子为对account的增删改查。在业务层,我们有AccountService这个接口。

//【1】表示为REST上下文的Controller
@RestEndpoint
public class AccountRestEndpoint {
    @Inject private AccountService accountService;

    //【2.1例子】对OPTIONS消息进行应答,学习ResponseEntity中如何设置header,status code以及body(本例为null)
    @RequestMapping(value = "account", method = RequestMethod.OPTIONS)
    public ResponseEntity<Void> discover(){
        HttpHeaders headers = new HttpHeaders();
        headers.add("Allow", "OPTIONS,HEAD,GET,POST");
        return new ResponseEntity<>(null, headers, HttpStatus.NO_CONTENT);
    }

    //【2.2例子】对于资源不存在,直接抛出异常,则根据@ControllerAdvice的处理返回结果,这种方式很简洁。
    @RequestMapping(value = "account/{id}", method = RequestMethod.OPTIONS)
    public ResponseEntity<Void> discover(@PathVariable("id") long id){
        if(this.accountService.getAccount(id) == null)
            throw new ResourceNotFoundException();
        
        HttpHeaders headers = new HttpHeaders();
        headers.add("Allow", "OPTIONS,HEAD,GET,POST");
        return new ResponseEntity<>(null, headers, HttpStatus.NO_CONTENT);
    }

    //【2.3例子】@ResponseBody可以直接返回对象,根据协商决定采用json还是xml的封装。 
    // 对于XML的封装,需要注意。我们应当尽量避免直接使用Collection或者Map,它们虽然很容易转换为json,但是很难转换为XML。本例定义了一个新的类AccountList。当然,我们也可以只使用JSON来规避这个问题。 
    @RequestMapping(value = "account", method = RequestMethod.GET)
    @ResponseBody @ResponseStatus(HttpStatus.OK)
    public AccountList read(){
        // 列表可能会很大,应当给予限制。小例子从略
        AccountList list = new AccountList();
        list.setValue(this.accountService.getAllAccounts());
        return list;
    }

    //【2.4例子】REST POST中返回Location信息。学习@RequestBody,请求消息体对象作为输入 
    @RequestMapping(value = "account", method = RequestMethod.POST)
    public ResponseEntity<Account> create(@RequestBody AccountForm form){
        // ...... 
        String uri = ServletUriComponentsBuilder.fromCurrentServletMapping()
                        .path("/account/{id}").buildAndExpand(account.getId()).toString();
        HttpHeaders headers = new HttpHeaders();
        headers.add("Location", uri);
        return new ResponseEntity<>(account, headers, HttpStatus.CREATED);
    }

    @XmlRootElement(name = "accounts")
    public static class AccountList{
        private List<Account> value;

        @XmlElement(name = "account")
        public List<Account> getValue() {
            return value;
        }

        public void setValue(List<Account> value) {
            this.value = value;
        }
    }
}

发现机制(XML封装)小例子

在keystone等RESTful接口中,我们经常会看到携带_link等表示相关url的信息,下面的小例子,将返回接口的URL。在HAL中,Json和XML有不同的接口,因此分开两个方法,通过produces(即请求中的Accept头)来匹配,在Accept中,按顺序具有优先级别。

返回Json的消息body为:

{
  "_links": {
    "account": {
      "href": "http://localhost:8080/chapter17/services/Rest/account"
    },
    "self": {
      "href": "http://localhost:8080/chapter17/services/Rest"
    }
  }
}
返回XML的消息body为
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resource>
   <link rel="http://localhost:8080/chapter17/services/Rest" href="self"/>
   <link rel="http://localhost:8080/chapter17/services/Rest/account" href="account"/>
</resource>
@RestEndpoint
public class IndexRestEndpoint {
    //produces attribute helps identify which method should be called based on the request's Accept header.
    // 这里也演示了Json使用的便捷。直接回复一个Map对象(而且是Map嵌套的)
    @RequestMapping(value = {"", "/"}, method = RequestMethod.GET, produces = {"application/json", "text/json"})
    @ResponseBody @ResponseStatus(HttpStatus.OK)
    public Map<String, Object> discoverJson(){
        ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentServletMapping();
        Map<String, JsonLink> links = new Hashtable<>(2);
        links.put("self", new JsonLink(builder.path("").build().toString()));
        links.put("account",new JsonLink(builder.path("/account").build().toString()));

        Map<String, Object> response = new Hashtable<>(1);
        response.put("_links", links);
        return response;        
    }

    /* 在XML中,需要在数据结构中,设置好@XmlRootElement,@XmlElement,@XmlAttribute,@XmlValue(本小例子没有使用到value的情况,这其实是缺省的情况),@XmlTransient(忽略)
    此外,我们还可以指定相应schema格式,例如:@XmlSchemaType(name = "base64Binary"),这里我的理解仅仅只是写明格式,并不会自动为你转换。对于byte[],在json和xml中会给出BASE64的String,也就是这里的base64Binary,如果你要采用其他格式,需要在代码中处理。
    Json的处理能力要强大,例如可以直接返回一个Instant对象,Json会自动给崔ISO8601的格式,而在XML中无相关显示,即便XmlSchemaType给出了date或者dateTime,还是需要在代码中手动处理 */
    @RequestMapping(value = {"", "/"}, method = RequestMethod.GET, produces = {"application/xml", "text/xml"})
    @ResponseBody @ResponseStatus(HttpStatus.OK)
    public Resource discoverXml(){
        ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentServletMapping();
        Resource resource = new Resource();
        resource.addLink(new Link("self", builder.path("").build().toString()));
        resource.addLink(new Link("account",builder.path("/account").build().toString()));
        return resource;
    }

    public static class JsonLink{
        private String href;

        public JsonLink(String href) {...}     
        @XmlAttribute
        public String getHref() {...}  
        public void setHref(String href) {...}
    }

    public static class Link extends JsonLink{
        private String rel;

        public Link(String href, String rel) {...}  
        @XmlAttribute
        public String getRel() {...} 
        public void setRel(String rel) {...}
    }

    @XmlRootElement
    public static class Resource{
        private List<Link> links = new ArrayList<>();

        @XmlElement(name = "link")
        public List<Link> getLinks() {...} 
        public void setLinks(List<Link> links) {...} 
        public void addLink(Link link){ this.links.add(link);}
    }
}

相关链接: 我的Professional Java for Web Applications相关文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值