2015-05-05 3 views
1

Я работаю над REST-API и столкнулся с проблемой архитектуры.Рекомендации для моделей REST-API

модель «Книга» представляет собой одну книгу со свойствами и функциями CRUD основе. Он загружается из базы данных с помощью функции чтения.

Однако, что если я хочу, чтобы получить все книги в базе данных? Текущая модель книги не охватывает этот вариант использования.

я пытался несколько подходов:

1.) вторая модель под названием 'Книга'. Он имеет функцию чтения, которая возвращает список объектов книги.

2.) Модель «Книга» сама по себе имеет функцию ReadAll которая загружает все книги.

3.) Модель «Книга» не является функциональным, он имеет только свойство. Вместо этого класс BookStorage загружает данные и заполняет одну или несколько моделей.

Я не удовлетворен ни одним из этих подходов. Существует ли наилучшая практика для этого сценария?

+2

Почему вы не удовлетворены ни одним из них? Номер 1 кажется хорошим вариантом. –

+1

Вы задаете вопрос о REST API, который обычно относится к интерфейсу службы (обычно HTTP + JSON или XML). Но вы также спрашиваете о классах и доступе к данным, который находится за интерфейсом HTTP-сервиса (на вашем родном языке программирования). Поэтому я думаю, что это поможет прояснить, является ли ваш вопрос интерфейсом службы/HTTP или за кодом службы. –

+0

@DavinTryon Я думаю, что это не очень интуитивно, особенно если к проекту присоединяются новые люди. также трудно читать, если у вас большая часть времени единственная и множественная модель (Book.php, Books.php, Author.php, Authors.php, ...) –

ответ

1

1.) Вторая модель называется 'Книга'. Он имеет функцию чтения, которая возвращает список объектов книги.

Это нормально, но пользователи и будущие разработчики проекта могут быть неясны в связи с тем, что у вас есть книга и книги. Поэтому обязательно задокументируйте API и прокомментируйте код. Кроме того, вам необходимо рассмотреть входные фильтры, чтобы ограничить результаты, или, по крайней мере, метод результатов страницы. Google однажды оценил, что было 130 миллионов книг, так что вы хотите получить все сразу?

например. SERVER/Книги /? SkipRecords = 0 & предел = 100

2.) Модель 'Книга' сама по себе имеет функцию ReadAll которая загружает все книги.

Это не идеальный вариант, поскольку он нарушает принцип единой ответственности. В основном это просто не имеет особого смысла в ситуации с ОО, чтобы иметь единственную книжную сущность, которая могла бы перечислить все ее дочерние сущности.

например. SERVER/TheHobbit/readAll .... yuck.

3.) Модель «Книга» нефункциональна, она обладает только свойствами. Вместо этого класс BookStorage загружает данные и заполняет одну или несколько моделей . Если вы можете предоставить эти функциональные расширения через API, то это тоже прекрасное решение. Все сводится к документации.

Может быть, это заканчивает тем, что, как это

например

SERVER/BookStorage/GetAllBooks? SkipRecords = 0 & предел = 100

SERVER/BookStorage/GetBook? Название = TheHobbit

+0

Есть ли у вас какие-либо другие идеи? или являются решениями 1 и 3 «только варианты». –

0

3.) Модель «Книга» нефункциональна, она обладает только свойствами. Вместо этого класс BookStorage загружает данные и заполняет одну или несколько моделей.

Этот подход похож на Repository Pattern и очень распространен. BookStorage будет BookRepository с интерфейсом типа:

public interface IBookRepository 
{ 
    Book GetById(int id); 
    IEnumerable<Book> GetAll(); 
    // Other methods for creating, updating and deleting books. 
} 

В контроллере вы бы тогда:

public class BooksController: ApiController 
{ 
    private readonly IBookRepository _bookRepository; 

    public BooksController(IBookRepository bookRepository) 
    { 
     _bookRepository = bookRepository; 
    } 

    [Route("api/books/{id}")] 
    public IHttpActionResult Get(int id) 
    { 
     var book = _bookRepository.GetById(id); 

     // ... 
    } 

    [Route("api/books")] 
    public IHttpActionResult Get() 
    { 
     var books = _bookRepository.GetAll(); 

     // ... 
    } 
} 

Если вы хотите подкачки, необходимо добавить фильтр, как Get(int page = 0), а затем в зависимости от вашего размер страницы, используйте что-то вроде bookRepository.GetAll().Skip(PAGE_SIZE * page).Take(PAGE_SIZE).

Модель не должна загружаться, поскольку она нарушает Single responsibility principle.

0

Отличный ресурс при проектировании веб-сервисов RESTful - статья Мартина Фаулера Richardson Maturity Model.

Суммируя для book случая:

  • использования POST /book создать книгу
  • использовать PUT /book обновить книгу
  • использование GET /book?author=Shakespeare&year=1602&someOtherParam=someValue найти книги
  • использование GET /book/:id для извлечения деталей конкретная книга
  • использовать DELETE /book/:id, чтобы удалить определенную книгу

Кроме того, вы можете следовать HATEOAS принципам, так что вам необходимо включить все соответствующие ссылки на книги в links собственность, так что клиенты вашего API не нужно будет строить свою собственную связь, когда они «Я хочу добавить/отредактировать/найти/удалить книгу.

Хотя простую на первый взгляд разработку хорошего веб-сервиса RESTful не так просто, однако HATEOAS помогает, поскольку изменения схемы URL-адресов на стороне сервера не влияют на клиентов, поскольку они не требуют жесткого кодирования URL-адресов, необходимых для операций CRUD. Все, что вам нужно сделать, это обеспечить отправную точку, например. базовый url, где все содержимое может быть обнаружено, и клиенты могут начать с него, перемещаясь через ваш веб-сервис.

+0

да ... но вопрос в том, как вы обрабатываете 'использовать GET/book? Author = Shakespeare & year = 1602 & someOtherParam = someValue, чтобы найти книги запрос в ваших моделях @Cristik –

+0

Запрос' search' может быть отображен на статическую метод 'find (query)', где 'query' соответствует словарю/карте значений ключа +. Это похоже на вариант № 2 из вашего вопроса – Cristik

0

Прочтите раздел CRUD Operations здесь. При разработке API REST вы найдете рекомендации по REST, которые должны соблюдаться.

Смежные вопросы