2016-02-01 17 views
19

con più controller di primavera che consumano e producono application/json, il mio codice è disseminato di annotazioni lunghi come:Primavera RequestMapping per i controller che producono e consumano JSON

C'è un modo per produrre un "composito/ereditata/annotazioni aggregate" con predefinite valori per consumes e produces, in modo tale che ho potuto invece scrivere qualcosa di simile:

@JSONRequestMapping(value = "/foo", method = RequestMethod.POST) 

Come facciamo a DEFI ne qualcosa come @JSONRequestMapping sopra? Notare che lo value e lo method sono passati come nel @RequestMapping, anche se è possibile passare in consumes o produces se il valore predefinito non è adatto.

Ho bisogno di controllare quello che sto tornando. Desidero i metodi di annotazione in modo da ottenere le intestazioni appropriate di Content-Type.

risposta

32

A partire dalla primavera 4.2.x, è possibile creare annotazioni di mappatura personalizzate utilizzando @RequestMapping come meta-annotazione. Quindi:

C'è un modo per produrre un "composito ereditata/aggregato /" annotazione con i valori di default per consuma e produce, in modo tale che io potrebbe invece scrivere qualcosa di simile:

@JSONRequestMapping(value = "/foo", method = RequestMethod.POST) 

Sì, c'è un modo. È possibile creare un meta annotazione come segue:

@Target({ElementType.METHOD, ElementType.TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
@RequestMapping(consumes = "application/json", produces = "application/json") 
public @interface JsonRequestMapping { 
    @AliasFor(annotation = RequestMapping.class, attribute = "value") 
    String[] value() default {}; 

    @AliasFor(annotation = RequestMapping.class, attribute = "method") 
    RequestMethod[] method() default {}; 

    @AliasFor(annotation = RequestMapping.class, attribute = "params") 
    String[] params() default {}; 

    @AliasFor(annotation = RequestMapping.class, attribute = "headers") 
    String[] headers() default {}; 

    @AliasFor(annotation = RequestMapping.class, attribute = "consumes") 
    String[] consumes() default {}; 

    @AliasFor(annotation = RequestMapping.class, attribute = "produces") 
    String[] produces() default {}; 
} 

Quindi è possibile utilizzare le impostazioni predefinite o addirittura ignorare loro come si desidera:

@JsonRequestMapping(method = POST) 
public String defaultSettings() { 
    return "Default settings"; 
} 

@JsonRequestMapping(value = "/override", method = PUT, produces = "text/plain") 
public String overrideSome(@RequestBody String json) { 
    return json; 
} 

Si può leggere di più su AliasFor in di javadoc e github wiki primavera.

+2

Questo è veramente interessante; non sapeva di questa nuova funzionalità. Il [JavaDoc per @AliasFor] (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AliasFor.html) è davvero buono. E c'è anche una [pagina wiki] (https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model) che descrive più dettagliatamente il modello di annotazione di Spring. – FGreg

+0

Grazie! Questo sembra l'informazione che stavo cercando. In 'public @interface JsonRequestMapping', è necessario ridichiarare 'name',' value', 'path',' method', 'params',' headers'? Mi interessa solo avere valori predefiniti per 'consumes',' produce'. –

+0

Sì, se si desidera sovrascrivere il valore corrispondente in 'RequestMapping', è necessario dichiararli nuovamente. –

0

Ci sono 2 le annotazioni in primavera: @RequestBody e @ResponseBody. Queste annotazioni consumano, rispettivamente producono JSON. Altre informazioni here.

+0

Questa è un'informazione interessante, ma non esattamente quello che sto cercando. Devo controllare quello che sto tornando. Voglio i metodi di annotazione 'produce' /' consuma' in modo da ottenere le intestazioni 'Content-Type' appropriate. –

6

È possibile utilizzare l'annotazione @RestController anziché @Controller.

13

La semplice risposta alla tua domanda è che non c'è Annotation-Inheritance in Java. Tuttavia, c'è un modo per usare le annotazioni Spring in un modo che penso possa aiutarti a risolvere il tuo problema.

@RequestMapping è supportato sia a livello di tipo che a livello di metodo.

Quando si inserisce @RequestMapping a livello di tipo, la maggior parte degli attributi è "ereditata" per ogni metodo in quella classe. Questo è mentioned nella documentazione di riferimento Spring. Guarda lo api docs per i dettagli su come ogni attributo viene gestito quando aggiungi @RequestMapping a un tipo. Ho riassunto questo per ogni attributo di seguito:

  • name: Valore a livello Type è concatenato con il valore a livello di metodo che utilizza '#' come separatore.
  • value: il valore a livello di tipo viene ereditato dal metodo.
  • path: il valore a livello di tipo viene ereditato dal metodo.
  • method: il valore a livello di tipo viene ereditato dal metodo.
  • params: il valore a livello di tipo viene ereditato dal metodo.
  • headers: il valore a livello di tipo viene ereditato dal metodo.
  • consumes: il valore a livello di Tipo viene sovrascritto dal metodo.
  • produces: il valore a livello di Tipo viene sovrascritto dal metodo.

Qui è un controller esempio breve che mette in mostra come è possibile utilizzare questo:

package com.example; 

import org.springframework.http.MediaType; 
import org.springframework.web.bind.annotation.*; 

@RestController 
@RequestMapping(path = "/", 
     consumes = MediaType.APPLICATION_JSON_VALUE, 
     produces = MediaType.APPLICATION_JSON_VALUE, 
     method = {RequestMethod.GET, RequestMethod.POST}) 
public class JsonProducingEndpoint { 

    private FooService fooService; 

    @RequestMapping(path = "/foo", method = RequestMethod.POST) 
    public String postAFoo(@RequestBody ThisIsAFoo theFoo) { 
     fooService.saveTheFoo(theFoo); 
     return "http://myservice.com/foo/1"; 
    } 

    @RequestMapping(path = "/foo/{id}", method = RequestMethod.GET) 
    public ThisIsAFoo getAFoo(@PathVariable String id) { 
     ThisIsAFoo foo = fooService.getAFoo(id); 
     return foo; 
    } 

    @RequestMapping(path = "/foo/{id}", produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET) 
    public ThisIsAFooXML getAFooXml(@PathVariable String id) { 
     ThisIsAFooXML foo = fooService.getAFoo(id); 
     return foo; 
    } 
} 
+2

Tecnicamente, la risposta di Ali Dehghani ha risposto meglio alla domanda che ho posto, quindi l'ho contrassegnata come accettata. Ma alla fine ho usato il tuo suggerimento, in un certo senso mi sono sembrato più pulito, quindi ti do anche una taglia, (+100, dato che non posso fare +50 due volte). Grazie. –

2

Non dovrebbe essere necessario per configurare le consuma o produce attributo a tutti. Spring fornirà automaticamente JSON in base ai seguenti fattori.

  • Il accetta intestazione della richiesta è application/json
  • @ResponseBody metodo annotato
  • biblioteca Jackson sul classpath

Si dovrebbe anche seguire il suggerimento di Wim e definire il controller con l'annotazione @RestController. Ciò ti eviterà di annotare ogni metodo di richiesta con @ResponseBody

Un altro vantaggio di questo approccio sarebbe se un client desidera XML invece di JSON, lo otterrebbero. Dovrebbero solo specificare xml nell'intestazione accept.

Problemi correlati