2015-07-07 4 views
9

Ich habe ein WidgetDto, das ich mit swagger UI-Annotationen versehen habe. Die letzte Antwort umschließt eine WidgetDtos-Liste mit einer Metadatenschicht (pro Seite 21 von this RESTful Best Practices-Dokument). Zum Beispiel:Dokumentieren einer eingepackten REST-Antwort mit Hilfe der Swagger-Benutzerschnittstelle

{ 
    "data" : [ 
    { 
     "id" : 1234, 
     "prop1" : "val1" 
     ... 
    }, 
    { 
     "id" : 5678, 
     "prop1" : "val2" 
     ... 
    }, 
    ... 
    ] 
} 

Mein sieht Java-Code wie folgt aus:

@GET 
@Produces(MediaType.APPLICATION_JSON) 
@ApiOperation(
     value = "Get all widgets.", 
     response = WidgetDto.class 
) 
@ApiResponses(value = { 
     @ApiResponse(code = 200, message = "Returns the list of widgets.") 
}) 
public Response getWidgets() { 
    List<WidgetDto> widgets; 
    ... 
    Map<String, Object> responseBody = new HashMap<>(); 
    responseBody.put("data", widgets); 
    return Response.ok(responseBody).build(); 
} 

Ich möchte dieses Muster auf mehrere Ressourcen wiederzuverwenden, und ich will nicht, Liste DTOs für jeden Antworttyp erstellen . Gibt es eine elegante Möglichkeit, Swagger zu verwenden, um diese Arten von Antwortkörpern zu dokumentieren?

+0

Können Sie eine Wrapper-Klasse erstellen, die eine Liste von "WidgetDto" in einem Feld "Daten" darstellt? –

+0

@tim_yates Ja, aber ich hoffe, dass es eine wiederverwendbare Lösung gibt, die es nicht erforderlich macht, spezifische Wrapper-Klassen für jeden DTO-Typ zu erstellen. – Bill

Antwort

0

Ihre Metadaten sind kein Teil Ihrer Ressource, aber sie sind Teil der Darstellung Ihrer Ressource.

In meinem Fall Antworten Typen sind ‚application/hal+jsonund ‚application/json‘, jeder von ihnen mit verschiedenen Metadaten einen anderen Wrapper verwenden. Um dieses Problem zu lösen, habe ich ein externes Dokument erstellt, um diese zwei Wrapper und für jede von ihnen zu erklären, wie eine einzelne Ressource und eine Liste von Ressourcen mit Metadaten dargestellt werden.

Ich glaube, meine Wahl ist richtig, weil ich die Ressource seiner Darstellungen trennen (pro Seite 7 'Manipulation von Ressourcen durch Vertretungen von this RESTful Best Practices-Dokument)

In Ihrem Fall geben Sie eine Liste von WidgetDtos Die Metadatenebene ist ein Teil der Darstellung Ihrer Ressource.

Sie können jedoch eine generische Klasse wie Ressourcen- und Ressourcen verwendet verwenden, indem spring-hateoas:

public class Resources<T> implements Iterable<T> { 
    private final Collection<T> content; 
    Resources(Iterable<T> content) { 
     this.content = new ArrayList<T>(); 
     for (T element : content) { 
      this.content.add(element); 
     } 
    } 
} 

Und es wie folgt verwenden:

@GET 
@Produces(MediaType.APPLICATION_JSON) 
@ApiOperation(
     value = "Get all widgets.", 
     response = WidgetDto.class 
) 
@ApiResponses(value = { 
     @ApiResponse(code = 200, message = "Returns the list of widgets.") 
}) 
public Response getWidgets() { 
    List<WidgetDto> widgets; 
    ... 
    return Response.ok(new Resources<WidgetDto>(widgets)).build(); 
} 
0

ich ein ähnliches Problem vor ein paar Monaten konfrontiert, wenn Ich habe ein Projekt für die Schule entwickelt. Die Lösung besteht darin, einen Umschlag zu erstellen und ihn immer zurückzugeben. Der Umschlag enthält ein Feld "Daten", das ein generisches ist; So können Sie es an jeden Datentyp binden. Beachten Sie, dass, obwohl ich es später verwendete ich gelesen habe, dass es Scarecly verwendet werden sollte (ich denke, Ihr Fall ist ein gutes Beispiel für die Verwendung), aber technisch sollte ein Ausnahmeobjekt geworfen werden, wenn die Anfrage fehlgeschlagen ist.

Auf jeden Fall ist dies meine Antwort-Klasse, die ich verwendet, um alle meine Antworten zurück:

public class Response <AnyData> { 

    private static final String SUCCESS = "success"; 
    private static final String FAILURE = "failure"; 

    private String status; 
    private AnyData data; 
    private String error; 

    private Response(String status, AnyData data, String error) { 
     this.status = status; 
     this.data = data; 
     this.error = error;; 
    } 

    private Response(String status, AnyData data) { 
     this(status, data,""); 
    } 

    private Response(String status, String error) { 
     this(status, null, error); 
    } 

    public static <AnyData> Response<AnyData> success(AnyData data) { 
     return new Response<AnyData>(SUCCESS, data); 
    } 

    public static <AnyData> Response<AnyData> failure(String error) { 
     return new Response<AnyData>(FAILURE, error); 
    } 

    public static <AnyData> Response<AnyData> unimplemented() { 
     return new Response<AnyData>(FAILURE, "Missing implementation in the backend."); 
    } 

    public static <AnyData> Response<AnyData> failureUserNotFound() { 
     return Response.failure("User not found!"); 
    } 

    public static <AnyData> Response<AnyData> failureBusinessNotFound() { 
     return Response.failure("Business not found!"); 
    } 

    // Removed getters and setters for simplicity. 
} 

Danach gesetzt ist, werden wir nur die Antworten erstellen direkt aus dem Auditor. Ich habe es ein bisschen geändert, damit es mit der Probe funktioniert, sollte genug lesbar sein. Beachten Sie, dass ich statische Methoden für meine Antworten: Erfolg() ", 'Fehler()' ...

@RestController 
@Api(tags={"Widgets"}) 
public class WidgetController { 

    @RequestMapping(value="/api/widgets", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON) 
    @ApiOperation(value = "Get all widgets.") 
    @ApiResponses(value = { 
      @ApiResponse(code = 200, message = "Returns the list of widgets.") 
    }) 
    public Response<List<WidgetDto>> getWidgets() { 
     List<WidgetDto> widgets = new LinkedList<>(); 
     widgets.add(new WidgetDto(1234, "val1")); 
     widgets.add(new WidgetDto(5678, "val2")); 

     return Response.success(widgets); 
    } 
} 

Und hier ist eine Probe des Antworttextes: enter image description here

this helps .