Если мы посмотрим, что пытается сделать OP, он пытается опубликовать два (возможно, несвязанных) объекта JSON. Прежде всего, любое решение попробовать и отправить одну часть в качестве тела, а одна часть как другой параметр IMO - это ужасные решения. Данные POST должны поступать в тело. Неправильно делать что-то только потому, что оно работает. Некоторые работы могут нарушать основные принципы REST.
Я вижу несколько решений
Другой вариант - просто использовать application/x-www-form-urlencoded
. Фактически мы можем иметь значения JSON. Для примера
curl -v http://localhost:8080/api/model \
-d 'one={"modelOne":"helloone"}' \
-d 'two={"modelTwo":"hellotwo"}'
public class ModelOne {
public String modelOne;
}
public class ModelTwo {
public String modelTwo;
}
@Path("model")
public class ModelResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String post(@FormParam("one") ModelOne modelOne,
@FormParam("two") ModelTwo modelTwo) {
return modelOne.modelOne + ":" + modelTwo.modelTwo;
}
}
Единственное, что нам нужно, чтобы заставить это работать, - это ParamConverterProvider
, чтобы заставить это работать. Ниже приведена версия, которая была реализована Михалом Гаджосом из команды Джерси (здесь была найдена ).
@Provider
public class JacksonJsonParamConverterProvider implements ParamConverterProvider {
@Context
private Providers providers;
@Override
public ParamConverter getConverter(final Class rawType,
final Type genericType,
final Annotation[] annotations) {
// Check whether we can convert the given type with Jackson.
final MessageBodyReader mbr = providers.getMessageBodyReader(rawType,
genericType, annotations, MediaType.APPLICATION_JSON_TYPE);
if (mbr == null
|| !mbr.isReadable(rawType, genericType, annotations, MediaType.APPLICATION_JSON_TYPE)) {
return null;
}
// Obtain custom ObjectMapper for special handling.
final ContextResolver contextResolver = providers
.getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);
final ObjectMapper mapper = contextResolver != null ?
contextResolver.getContext(rawType) : new ObjectMapper();
// Create ParamConverter.
return new ParamConverter() {
@Override
public T fromString(final String value) {
try {
return mapper.reader(rawType).readValue(value);
} catch (IOException e) {
throw new ProcessingException(e);
}
}
@Override
public String toString(final T value) {
try {
return mapper.writer().writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new ProcessingException(e);
}
}
};
}
}
Если вы не просматриваете ресурсы и поставщиков, просто зарегистрируйте этого поставщика, и приведенный выше пример должен работать.
Одно из решений, о котором никто не упомянул, заключается в использовании multipart . Это позволяет нам отправлять произвольные части в запрос. Поскольку каждый запрос может иметь только один объект, многопроцессор - это работа вокруг, поскольку он позволяет иметь разные части (с их собственными типами контента) как часть тела сущности.
Вот пример использования Джерси (см. официальный doc здесь )
Зависимость
org.glassfish.jersey.media
jersey-media-multipart
${jersey-2.x.version}
Зарегистрируйте класс MultipartFeature
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
@ApplicationPath("/api")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("stackoverflow.jersey");
register(MultiPartFeature.class);
}
}
Resource
g32]
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.media.multipart.FormDataParam;
@Path("foobar")
public class MultipartResource {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postFooBar(@FormDataParam("foo") Foo foo,
@FormDataParam("bar") Bar bar) {
String response = foo.foo + "; " + bar.bar;
return Response.ok(response).build();
}
public static class Foo {
public String foo;
}
public static class Bar {
public String bar;
}
}
Теперь сложная часть с некоторыми клиентами заключается в том, что не существует способа установить Content-Type
каждой части тела, что требуется для работы вышеприведенного. Многостраничный провайдер будет искать устройство чтения сообщений, основанное на типе каждой части. Если он не установлен на application/json
или тип, для Foo
или Bar
будет читатель, это не удастся. Мы будем использовать JSON здесь. Нет никакой дополнительной конфигурации, кроме как иметь доступный читатель. Я буду использовать Джексона. При использовании следующей зависимости никакая другая конфигурация не требуется, так как поставщик будет обнаружен через сканирование классов.
org.glassfish.jersey.media
jersey-media-json-jackson
${jersey-2.x.version}
Теперь тест. Я буду использовать cURL . Вы можете видеть, что я явно установил Content-Type
для каждой части с type
. -F
означает разную часть. (См. Очень низкую позицию для представления о том, как выглядит тело запроса.)
blockquote>
curl -v -X POST \ -H "Content-Type:multipart/form-data" \ -F "bar={\"bar\":\"BarBar\"};type=application/json" \ -F "foo={\"foo\":\"FooFoo\"};type=application/json" \ http://localhost:8080/api/foobar
Результат:FooFoo; BarBar
Результат как мы и ожидали. Если вы посмотрите на метод ресурса, все, что мы делаем, это вернуть эту строку
foo.foo + "; " + bar.bar
, собранную из двух объектов JSON.Вы можете увидеть некоторые примеры, используя несколько разных клиентов JAX-RS, в ссылках ниже , Вы также увидите пример на стороне сервера из этих различных реализаций JAX-RS. В каждой ссылке должно быть где-то в нем ссылка на официальную документацию для этой реализации
Там есть другие реализации JAX-RS, но вам нужно будет найти документацию для него самостоятельно. Вышеупомянутые три являются единственными, с которыми у меня есть опыт.
Что касается клиентов Javascript, то большинство примеров, которые я вижу (например, , некоторые из этих включают установку
Content-Type
на undefined / false (используяFormData
), позволяя обозревателю обработать его, но это не сработает для нас, так как браузер не будет устанавливатьContent-Type
для каждой части. И тип по умолчанию -text/plain
.Я уверен, что есть библиотеки, которые позволяют устанавливать тип для каждой части, но просто чтобы показать вам, как это можно сделать вручную, я отправлю пример (получил небольшую помощь от здесь . Я буду использовать Angular, но ворчание работы по созданию тела объекта будет простым старым Javascript.
{{result}}
Интересной частью является функция
createRequest
. где мы построим multipart, установивContent-Type
каждой части вapplication/json
и объединив стробированные объектыfoo
иbar
к каждой части. Если вы не знакомы с многостраничным см. здесь для получения дополнительной информации . Другая интересная часть - это заголовок. t доmultipart/form-data
.Ниже приведен результат. В Angular я использовал результат, чтобы показать в HTML, с
$scope.result = response.data
. Кнопка, которую вы видите, просто должна была сделать запрос. Вы также увидите данные запроса в firebug[/g15]
3. Просто заверните их в один родительский объект
Этот параметр должен быть понятным, как это уже упоминалось другими.