Akka Http возвращает 404 не найдено

Я пытаюсь добиться очень простой вещи.

Скажем, у меня есть REST API. Когда я звоню

/api/recipe/1

Я хотел бы, чтобы ресурс был возвращен в виде json.

Когда я ударил

/api/recipe/2

должен быть возвращен HTTP-ответ 404 Not Found. Просто как тот.

Очевидно, я что-то упускаю из виду, как работают директивы маршрутизации, поскольку я не могу составить их с учетом вышеупомянутой логики.

К сожалению, мне не удалось найти конкретного примера, а официальная документация не особенно полезна.

Я пытаюсь что-то вроде этого, но код дает ошибку компиляции:

class RecipeResource(recipeService: RecipeService)(implicit executionContext: ExecutionContext) extends DefaultJsonProtocol {

  implicit val recipeFormat = jsonFormat1(Recipe.apply)

  val routes = pathPrefix("recipe") {
    (get & path(LongNumber)) { id =>
      complete {
        recipeService.getRecipeById(id).map {
          case Some(recipe) => ToResponseMarshallable(recipe)
          // type mismatch here, akka.http.scaladsl.marshalling.ToResponseMarshallable 
          // is required
          case None => HttpResponse(StatusCodes.NotFound)
        }
      }
    }
  }
}

Обновлять

Вот код recipeService для большей ясности:

class RecipeService(implicit executionContext: ExecutionContext) {

  def getRecipeById(id: Long): Future[Option[Recipe]] = {
    id match {
      case 1 => Future.successful(Some(Recipe("Imperial IPA")))
      case _ => Future.successful(None)
    }
  }
}

Ошибка компиляции, которую я получаю:

[error] /h......../....../...../RecipeResource.scala:22: type mismatch;
[error]  found   : scala.concurrent.Future[Object]
[error]  required: akka.http.scaladsl.marshalling.ToResponseMarshallable
[error]         recipeService.getRecipeById(id).map {
[error]                                             ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed

Обновление 2

Основываясь на ответе leachbj, я избавился от ненужного сопоставления с образцом в маршруте. Теперь код компилируется и выглядит так:

class RecipeResource(recipeService: RecipeService)(implicit executionContext: ExecutionContext) extends DefaultJsonProtocol {

  implicit val recipeFormat = jsonFormat1(Recipe.apply)

  val routes = pathPrefix("recipe") {
    (get & path(LongNumber)) { id =>
      complete(recipeService.getRecipeById(id))
    }
  }
}

Когда рецепт существует (например, /api/recipe/1), я получаю ответ JSON и 200 OK, что и ожидалось.

Теперь в случае несуществующего ресурса (например, /api/recipe/2) ответ будет пустым, но будет получен код состояния 200 OK.

Мой вопрос в том, как я могу настроить akka-http, чтобы иметь возможность complete(Future[None[T]]) возвращать 404 Not found.

Я ищу общий подход, который будет работать для любого возвращаемого значения Future[None].


person David Siro    schedule 19.08.2016    source источник
comment
Каков тип возврата getRecipeById?   -  person M.K.    schedule 20.08.2016
comment
Итак, что вы получаете, когда запускаете запрос для этого идентификатора? Вы получаете None?   -  person M.K.    schedule 20.08.2016
comment
У меня есть простая заглушка, которая возвращает Some в случае, если id равен 1, и None в противном случае. Код добавлен в пост.   -  person David Siro    schedule 20.08.2016
comment
Какая ошибка компилятора? Вы можете опубликовать это?   -  person M.K.    schedule 20.08.2016
comment
В основном он ожидает akka.http.scaladsl.marshalling.ToResponseMarshallable как возвращаемый тип закрытия. Вывод компилятора добавлен в пост.   -  person David Siro    schedule 20.08.2016
comment
Вы возвращаете два несовместимых типа. У вас должен быть неявный маршаллер в области видимости. Звонок ToResponseMarshallable — это запах, который я здесь чувствую. РЕДАКТИРОВАТЬ: я только что взглянул на часть своего кода Akka-Http... Я возвращаю кортеж StatusCode -> response и вообще не заморачиваюсь с ToResponseMarshallable.   -  person Chris Martin    schedule 20.08.2016
comment
Не могли бы вы в конечном итоге опубликовать какой-нибудь код маршрутизации, который условно возвращает: а) объект, который позже маршалируется в ответ, б) пустой ответ 404? Это бы очень помогло, ура.   -  person David Siro    schedule 20.08.2016


Ответы (1)


Если вы complete(Future[Option[T]]) и есть подходящий Json Marshaller, Akka вернет ответ как json, если значение равно Some(v), или пустой ответ 200 для None. Если вы используете спрей-json, создайте неявный RootJsonFormat[T] и добавьте import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._. Аналогичные имплициты поддержки существуют и для других библиотек сортировки.

Чтобы сгенерировать 404 для None, вам нужно обернуть complete rejectEmptyResponse.

person leachbj    schedule 21.08.2016
comment
Спасибо, я сделал рефакторинг кода, как было предложено. Теперь в случае Some я получаю JSON и 200 ОК, а в случае None я получаю явно не ответ, но все же 200 ОК. Теперь интересно, это как-то по умолчанию в akka-http или его можно настроить. - person David Siro; 25.08.2016
comment
Пожалуйста, смотрите обновленный текст, вам нужна директива rejectEmptyResponse. - person leachbj; 26.08.2016