2

对 ServiceStack 的 Swagger 插件有一些问题。我已经为我的服务配置了路由描述,但生成的 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()
        };
    }
}

Swagger 界面似乎已正确拾取元素:

在此处输入图像描述

结果是 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。

谁能看到我做错了什么?

4

2 回答 2

4

我看到的几个问题...

  • 我认为您正在尝试使用 Swagger-UI 模仿表单帖子。这个功能刚刚在上个月被添加到 swagger ( https://github.com/wordnik/swagger-core/issues/69 ) 中,所以我认为它在 Nuget 下载中不可用。

  • 您看到的是一个空正文,因为您的 ParameterType 是 DTO 上每个属性的“正文”。Swagger 想要一个包含整个请求正文内容的“正文”值。它不会循环遍历您的每个“正文”属性来组成正文内容。如果您填充 Rating 文本框,您应该会在请求中看到它的值(Rating 是您的最后一个带有 'body' ParameterType 的属性)。

请参阅此处的示例http://petstore.swagger.wordnik.com/#!/pet/addPet_post_1

使用 Chrome 或 Firebug,您可以在 swagger-ui.js 中的第 1182 行周围设置断点,以查看它如何构建请求正文内容(bodyParam 变量)

于 2013-02-23T04:38:55.517 回答
3

请注意,最新版本的 ServiceStack.Api.Swagger 对 Swagger 中的请求正文文档的支持有了很大改进。要遵循当前的最佳实践,请确保从 NuGet 更新 ServiceStack.Api.Swagger(以及所有其他 ServiceStack 包)。确保在 Api.Swagger 更新中合并 HTML/JS/CSS 文件。将所有ApiMember属性替换为简单Description属性 (System.ComponentModel.DescriptionAttribute)。您不再需要 ApiMember 属性中的 Name 或 DataType 属性,因为 Swagger 代码将通过对您的请求 DTO 进行反射来自动检测到这一点。

请注意,使用最新的代码,您根本不需要任何ApiMember属性ParameterType = "body"。如果您没有此类属性,它将自动生成具有正确数据类型和文档的 Swagger 请求正文文本区域。

您可能需要为该ApiMember属性重新添加一个属性IdParameterType = "path"Verb = "PUT"正确记录您的 PUT 请求。

于 2013-07-26T12:42:45.113 回答