2013-02-22 38 views
2

我在Swagger plugin to ServiceStack有一些問題。我爲我的服務配置了路由描述,但生成的POST不包含正文。Swagger for ServiceStack POST空主體

我的服務是這樣的:

/// <summary> 
/// Define your ServiceStack web service request (i.e. Request DTO). 
/// </summary> 
/// <remarks>The route is defined here rather than in the AppHost.</remarks> 
[Api("GET or DELETE a single movie by Id. Use POST to create a new Movie and PUT to update it")] 
[Route("/movie", "POST", Summary = @"POST a new movie", Notes = "Send a movie here")] 
[Route("/movie/{Id}", "GET,PUT,DELETE", Summary = @"GET, PUT, or DELETE a movie", Notes = "GET a specific movie by Id, or PUT a thing, or delete a movie")] 
public class Movie 
{ 
    /// <summary> 
    /// Initializes a new instance of the movie. 
    /// </summary> 
    public Movie() 
    { 
     this.Genres = new List<string>(); 
    } 

    /// <summary> 
    /// Gets or sets the id of the movie. The id will be automatically incremented when added. 
    /// </summary> 
    //[AutoIncrement] 
    [ApiMember(Name = "Id", Description = "The Id of this movie", ParameterType = "body", DataType = "string", IsRequired = false)] 
    public string Id { get; set; } 

    [ApiMember(Name = "ImdbId", Description = "The ImdbId of this movie", ParameterType = "body", DataType = "string", IsRequired = false)] 
    public string ImdbId { get; set; } 

    [ApiMember(Name = "Title", Description = "The Title of this movie", ParameterType = "body", DataType = "string", IsRequired = false)] 
    public string Title { get; set; } 

    [ApiMember(Name = "Rating", Description = "The Rating of this movie", ParameterType = "body", DataType = "decimal", IsRequired = false)] 
    public decimal Rating { get; set; } 

    [ApiMember(Name = "Director", Description = "The Director of this movie", ParameterType = "string", DataType = "string", IsRequired = false)] 
    public string Director { get; set; } 

    [ApiMember(Name = "ReleaseDate", Description = "The ReleaseDate of this movie", ParameterType = "string", DataType = "Date", IsRequired = false)] 
    public DateTime ReleaseDate { get; set; } 

    [ApiMember(Name = "TagLine", Description = "The TagLine of this movie", ParameterType = "string", DataType = "string", IsRequired = false)] 
    public string TagLine { get; set; } 

    [ApiMember(Name = "Genres", Description = "The Genres of this movie", ParameterType = "string", DataType = "string", IsRequired = false)] 
    public List<string> Genres { get; set; } 
} 

/// <summary> 
/// Define your ServiceStack web service response (i.e. Response DTO). 
/// </summary> 
public class MovieResponse 
{ 
    /// <summary> 
    /// Gets or sets the movie. 
    /// </summary> 
    public Movie Movie { get; set; } 
} 

/// <summary> 
/// Create your ServiceStack restful web service implementation. 
/// </summary> 
public class MovieService : Service 
{ 
    public IMovieRepository MovieRepository { get; set; } 

    /// <summary> 
    /// GET /movies/{Id} 
    /// </summary> 
    public MovieResponse Get(Movie movie) 
    { 
     var item = MovieRepository.FindOne(new ObjectId(movie.Id)); 

     return new MovieResponse 
     { 
      Movie = item, 
     }; 
    } 

    /// <summary> 
    /// POST /movies 
    /// 
    /// returns HTTP Response => 
    ///  201 Created 
    ///  Location: http://localhost/ServiceStack.MovieRest/movies/{newMovieId} 
    ///  
    ///  {newMovie DTO in [xml|json|jsv|etc]} 
    /// 
    /// </summary> 
    public object Post(Movie movie) 
    { 
     MovieRepository.Save(movie); 
     var newMovieId = movie.Id; 

     var newMovie = new MovieResponse 
     { 
      Movie = MovieRepository.FindOne(new ObjectId(movie.Id)) 
     }; 

     return new HttpResult(newMovie) 
     { 
      StatusCode = HttpStatusCode.Created, 
      Headers = { 
       { HttpHeaders.Location, base.Request.AbsoluteUri.CombineWith(newMovieId) } 
      } 
     }; 
    } 

    /// <summary> 
    /// PUT /movies/{id} 
    /// </summary> 
    public object Put(Movie movie) 
    { 
     MovieRepository.Save(movie); 

     return new HttpResult 
     { 
      StatusCode = HttpStatusCode.NoContent, 
      Headers = { 
       { HttpHeaders.Location, this.RequestContext.AbsoluteUri.CombineWith(movie.Id) } 
      } 
     }; 
    } 

    /// <summary> 
    /// DELETE /movies/{Id} 
    /// </summary> 
    public object Delete(Movie request) 
    { 
     MovieRepository.Remove(new ObjectId(request.Id)); 

     return new HttpResult 
     { 
      StatusCode = HttpStatusCode.NoContent, 
      Headers = { 
       { HttpHeaders.Location, this.RequestContext.AbsoluteUri.CombineWith(request.Id) } 
      } 
     }; 
    } 
} 

/// <summary> 
/// Define your ServiceStack web service request (i.e. Request DTO). 
/// </summary> 
/// <remarks>The route is defined here rather than in the AppHost.</remarks> 
[Api("Find movies by genre, or all movies if no genre is provided")] 
[Route("/movies", "GET, OPTIONS")] 
[Route("/movies/genres/{Genre}")] 
public class Movies 
{ 
    public string Genre { get; set; } 
} 

/// <summary> 
/// Define your ServiceStack web service response (i.e. Response DTO). 
/// </summary>  
public class MoviesResponse 
{ 
    /// <summary> 
    /// Gets or sets the list of movies. 
    /// </summary> 

    public List<Movie> Movies { get; set; } 
} 

/// <summary> 
/// Create your ServiceStack RESTful web service implementation. 
/// </summary> 
public class MoviesService : Service 
{ 
    public IMovieRepository MovieRepository { get; set; } 

    /// <summary> 
    /// GET /movies 
    /// GET /movies/genres/{Genre} 
    /// </summary> 
    public object Get(Movies request) 
    { 
     return new MoviesResponse 
     { 
      Movies = MovieRepository.FindAll().ToList() 
     }; 
    } 
} 

招搖界面看上去已經正確拿起元素:

enter image description here

結果是500錯誤:

POST http://localhost:57853/movie HTTP/1.1 
Host: localhost:57853 
Connection: keep-alive 
Content-Length: 0 
Accept: application/json, text/javascript, */*; q=0.01 
Origin: http://localhost:57853 
X-Requested-With: XMLHttpRequest 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17 
Content-Type: application/json 
Referer: http://localhost:57853/swagger-ui/index.html 
Accept-Encoding: gzip,deflate,sdch 
Accept-Language: en-GB,en;q=0.8,en-US;q=0.6 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 

Swagger未附加POST正文。內容長度:0。結果是一個空的請求對象,導致NullReferenceException。

任何人都可以看到我做錯了什麼嗎?

+0

好的,我可以看到這裏的身體是錯誤的類型。如果我將它們設置回原始類型,仍然沒有快樂。 – Junto 2013-02-22 17:32:17

回答

4

一對夫婦的問題,我看到了

  • 我想你想模仿使用揚鞭的UI表單POST。這個功能在上個月剛剛添加到swagger(https://github.com/wordnik/swagger-core/issues/69)中,所以我不認爲它在Nuget下載中可用。

  • 由於您的DTO上的每個Property的ParameterType都是「body」,因此您會看到空的主體。 Swagger需要一個包含整個請求主體內容的「主體」值。它不循環每個「body」屬性來組成正文內容。如果您填充評級文本框,您應該在請求中看到它的值(評級是您最後一次使用'body'參數類型的屬性)。

看到這裏的例子http://petstore.swagger.wordnik.com/#!/pet/addPet_post_1

使用Chrome或螢火蟲可以設置在左右招搖,ui.js線#1182斷點,看看它是如何建立請求主體內容(bodyParam變量)

3

請注意,最新版本的ServiceStack.Api.Swagger在Swagger中對請求正文文檔的支持有了很大改進。要遵循當前的最佳做法,請確保從NuGet更新ServiceStack.Api.Swagger(以及所有其他ServiceStack包)。確保您在Api.Swagger更新中的HTML/JS/CSS文件中合併。用簡單的Description屬性(System.ComponentModel.DescriptionAttribute)替換所有ApiMember屬性。您不再需要ApiMember屬性中的Name或DataType屬性,因爲Swagger代碼將通過對請求DTO進行反射來自動檢測此屬性。

請注意,使用最新的代碼,您根本不需要具有ParameterType = "body"的任何ApiMember屬性。如果你沒有這樣的屬性,它會自動生成一個帶有正確數據類型和文檔的Swagger請求主體textarea。

您可能需要添加一個ApiMember屬性回來了Id財產,與ParameterType = "path"Verb = "PUT"正確記錄您的PUT請求。