program tip

JAX-RS 다중 오브젝트 게시

radiobox 2020. 10. 23. 07:44
반응형

JAX-RS 다중 오브젝트 게시


방법이 있습니다.

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(ObjectOne objectOne, ObjectTwo objectTwo)

이제 json 형식으로 단일 객체를 게시 할 수 있다는 것을 알고 있습니다. 그러나 여러 개체를 수행 할 수 있습니까? 그렇다면 어떻게?


대답은 아니오 입니다.

이유는 간단합니다. 이것은 메소드에서받을 수있는 매개 변수에 관한 것입니다. 요청과 관련이 있어야합니다. 권리? 따라서 헤더 나 쿠키, 쿼리 매개 변수, 매트릭스 매개 변수, 경로 매개 변수 또는 요청 본문 이어야합니다 . (완전히 이야기하기 위해 컨텍스트라고하는 추가 매개 변수 유형이 있습니다.)

이제 요청에서 JSON 객체를 수신하면 요청 본문 에서 수신합니다 . 요청에 몇 개의 본문이있을 수 있습니까? 단 하나. 따라서 하나의 JSON 개체 만받을 수 있습니다.


당신은 할 수없는 등 제대로 Tarlog에 의해 언급이처럼 방법을 사용합니다.

그러나 다음과 같이 할 수 있습니다.

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(List<ObjectOne> objects)

아니면 이거:

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(BeanWithObjectOneAndObjectTwo containerObject)

또한 항상 메소드를 GET 매개 변수와 결합 할 수 있습니다.

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(List<ObjectOne> objects, @QueryParam("objectTwoId") long objectTwoId)

OP가 무엇을 하려는지 살펴보면, 그는 두 개의 (아마도 관련이없는) JSON 객체를 게시하려고합니다. 먼저 한 부분을 본문으로 보내고 한 부분을 다른 매개 변수 인 IMO로 보내는 모든 솔루션은 끔찍한 솔루션입니다. POST 데이터는 본문에 있어야합니다. 그것이 효과가 있다는 이유만으로 무언가를하는 것은 옳지 않습니다. 일부 해결 방법은 기본 REST 원칙을 위반할 수 있습니다.

몇 가지 해결책을 봅니다

  1. application / x-www-form-urlencoded 사용
  2. Multipart 사용
  3. 단일 부모 개체로 포장하십시오.

1. application / x-www-form-urlencoded 사용

또 다른 옵션은 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을 수행하는 것입니다. 아래는 Jersey Team의 Michal Gadjos가 구현 한 것입니다 ( 여기에서 설명과 함께 발견 ).

@Provider
public class JacksonJsonParamConverterProvider implements ParamConverterProvider {

    @Context
    private Providers providers;

    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType,
                                              final Type genericType,
                                              final Annotation[] annotations) {
        // Check whether we can convert the given type with Jackson.
        final MessageBodyReader<T> 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<ObjectMapper> contextResolver = providers
                .getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);

        final ObjectMapper mapper = contextResolver != null ?
                contextResolver.getContext(rawType) : new ObjectMapper();

        // Create ParamConverter.
        return new ParamConverter<T>() {

            @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);
                }
            }
        };
    }
}

리소스 및 공급자를 검색하지 않는 경우이 공급자를 등록하기 만하면 위의 예제가 작동합니다.

2. Multipart 사용

아무도 언급하지 않은 한 가지 해결책은 multipart 를 사용하는 것 입니다. 이를 통해 요청에서 임의의 부분을 보낼 수 있습니다. 각 요청은 하나의 엔티티 본문 만 가질 수 있으므로 다중 부분은 엔티티 본문의 일부로 다른 부분 (자체 콘텐츠 유형 포함)을 가질 수 있으므로 해결 방법입니다.

다음은 Jersey를 사용한 예입니다 ( 여기에서 공식 문서 참조 ).

의존

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>${jersey-2.x.version}</version>
</dependency>

등록 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);
    }
}

리소스 클래스

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을 사용합니다. 추가 구성은 없지만 리더를 사용할 수 있습니다. 잭슨을 사용하겠습니다. 아래의 종속성을 사용하면 공급자가 클래스 경로 스캔을 통해 검색되므로 다른 구성이 필요하지 않습니다.

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey-2.x.version}</version>
</dependency>

이제 테스트입니다. 나는 cURL을 사용할 것 입니다. 를 사용하여 Content-Type각 부분에 대해 명시 적으로 설정 한 것을 볼 수 있습니다 type. -F다른 부분을 의미한다. (요청 본문이 실제로 어떻게 보이는지에 대한 아이디어는 게시물의 맨 아래를 참조하십시오.)

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 구현이 있지만 이에 대한 문서를 직접 찾아야합니다. 위의 세 가지는 내가 경험 한 유일한 것입니다.

자바 스크립트 클라이언트에 관해서는 대부분의 예제를 볼 수 있습니다 (예 :이 중 일부Content-Type 는를 정의되지 않음 / 거짓으로 설정 (사용 FormData)하여 브라우저가 처리하도록합니다. 그러나 브라우저가 Content-Type그리고 기본 유형은 text/plain입니다.

각 부분에 대한 유형을 설정할 수있는 라이브러리가 있지만 수동으로 수행하는 방법을 보여주기 위해 예제를 게시 할 것입니다 ( 여기 에서 약간의 도움을 얻었 습니다 . Angular를 사용할 것입니다. , 그러나 엔티티 본문을 작성하는 지저분한 작업은 평범한 오래된 Javascript가 될 것입니다.

<!DOCTYPE html>
<html ng-app="multipartApp">
    <head>
        <script src="js/libs/angular.js/angular.js"></script>
        <script>
            angular.module("multipartApp", [])
            .controller("defaultCtrl", function($scope, $http) {

                $scope.sendData = function() {
                    var foo = JSON.stringify({foo: "FooFoo"});
                    var bar = JSON.stringify({bar: "BarBar"});

                    var boundary = Math.random().toString().substr(2);                    
                    var header = "multipart/form-data; charset=utf-8; boundary=" + boundary;

                    $http({
                        url: "/api/foobar",
                        headers: { "Content-Type": header }, 
                        data: createRequest(foo, bar, boundary),
                        method: "POST"
                    }).then(function(response) {
                        $scope.result = response.data;
                    });
                };

                function createRequest(foo, bar, boundary) {
                    var multipart = "";
                    multipart += "--" + boundary
                        + "\r\nContent-Disposition: form-data; name=foo"
                        + "\r\nContent-type: application/json"
                        + "\r\n\r\n" + foo + "\r\n";        
                    multipart += "--" + boundary
                        + "\r\nContent-Disposition: form-data; name=bar"
                        + "\r\nContent-type: application/json"
                        + "\r\n\r\n" + bar + "\r\n";
                    multipart += "--" + boundary + "--\r\n";
                    return multipart;
                }
            });
        </script>
    </head>
    <body>
        <div ng-controller="defaultCtrl">
            <button ng-click="sendData()">Send</button>
            <p>{{result}}</p>
        </div>
    </body>
</html>

흥미로운 부분은 createRequest기능입니다. 여기에서 멀티 파트를 빌드하고 Content-Type각 파트의를로 설정 application/json하고 스트링 화 foobar객체를 각 파트에 연결합니다 . 멀티 파트 형식에 익숙하지 않은 경우 여기에서 자세한 정보를 참조하십시오 . 또 다른 흥미로운 부분은 헤더입니다. 로 설정했습니다 multipart/form-data.

다음은 결과입니다. Angular에서는 결과를 사용하여 $scope.result = response.data. 보이는 버튼은 요청을하기위한 것입니다. 또한 firebug 에서 요청 데이터를 볼 수 있습니다.

여기에 이미지 설명 입력

3. 단일 부모 개체로 포장

다른 사람들이 이미 언급했듯이이 옵션은 자명해야합니다.


다음 접근 방식은 일반적으로 다음과 같은 경우에 적용됩니다.

TransferObject {
    ObjectOne objectOne;
    ObjectTwo objectTwo;

    //getters/setters
}

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(TransferObject object){
//        object.getObejctOne()....
}

Tarlog에서 설명한대로 단일 POST 호출에 두 개의 개별 개체를 넣을 수 없습니다.

어쨌든 처음 두 개체를 포함하는 세 번째 컨테이너 개체를 만들고 POS 호출 내에서 해당 개체를 전달할 수 있습니다.


나는 또한 이러한 문제에 직면했습니다. 아마도 이것이 도움이 될 것입니다.

@POST
@Path("/{par}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Object centralService(@PathParam("par") String operation, Object requestEntity) throws JSONException {

    ObjectMapper objectMapper=new ObjectMapper();

    Cars cars = new Cars();  
    Seller seller = new Seller();
    String someThingElse;

    HashMap<String, Object> mapper = new HashMap<>(); //Diamond )))

    mapper = (HashMap<String, Object>) requestEntity;

    cars=objectMapper.convertValue(mapper.get("cars"), Cars.class);
    seller=objectMapper.convertValue(mapper.get("seller"), Seller.class);
    someThingElse=objectMapper.convertValue(mapper.get("someThingElse"), String.class);

    System.out.println("Cars Data "+cars.toString());

    System.out.println("Sellers Data "+seller.toString());

    System.out.println("SomeThingElse "+someThingElse);

    if (operation.equals("search")) {
        System.out.println("Searching");
    } else if (operation.equals("insertNewData")) {
        System.out.println("Inserting New Data");
    } else if (operation.equals("buyCar")) {
        System.out.println("Buying new Car");
    }

    JSONObject json=new JSONObject();
    json.put("result","Works Fine!!!");


    return json.toString();

}


*******CARS POJO********@XmlRootElement for Mapping Object to XML or JSON***

@XmlRootElement
public class Cars {
    private int id;
    private String brand;
    private String model;
    private String body_type;
    private String fuel;
    private String engine_volume;
    private String horsepower;
    private String transmission;
    private String drive;
    private String status;
    private String mileage;
    private String price;
    private String description;
    private String picture;
    private String fk_seller_oid;
    } // Setters and Getters Omitted 

*******SELLER POJO********@XmlRootElement for Mapping Object to XML or JSON***

@XmlRootElement
public class Seller {
    private int id;
    private String name;
    private String surname;
    private String phone;
    private String email;
    private String country;
    private String city;
    private String paste_date;
    }//Setters and Getters omitted too


*********************FRONT END Looks Like This******************

$(function(){
$('#post').on('click',function(){
        console.log('Begins');
        $.ajax({
            type:'POST',
            url: '/ENGINE/cars/test',
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            data:complexObject(),
            success: function(data){
                console.log('Sended and returned'+JSON.stringify(data));
            },
            error: function(err){
                console.log('Error');
                console.log("AJAX error in request: " + JSON.stringify(err, null, 2));
            }
        }); //-- END of Ajax
        console.log('Ends POST');
        console.log(formToJSON());

    }); // -- END of click function   POST


function complexObject(){
    return JSON.stringify({
                "cars":{"id":"1234","brand":"Mercedes","model":"S class","body_type":"Sedan","fuel":"Benzoline","engine_volume":"6.5",
                "horsepower":"1600","transmission":"Automat","drive":"Full PLag","status":"new","mileage":"7.00","price":"15000",
                "description":"new car and very nice car","picture":"mercedes.jpg","fk_seller_oid":"1234444"},
        "seller":{  "id":"234","name":"Djonotan","surname":"Klinton","phone":"+994707702747","email":"email@gmail.com",                 "country":"Azeribaijan","city":"Baku","paste_date":"20150327"},
        "someThingElse":"String type of element"        
    }); 
} //-- END of Complex Object
});// -- END of JQuery -  Ajax

개체 배열을 허용하도록 선언 된 POST 메서드를 사용하여 수행 할 수 있습니다. 이와 같은 예

T[] create(@RequestBody T[] objects) {
for( T object : objects ) {
   service.create(object);
  }
}

@Consumes (MediaType.APPLICATION_JSON)을 @Consumes ({MediaType.APPLICATION_FORM_URLENCODED})로 변경 한 다음 여러 개체를 본문에 넣을 수 있습니다.

참고 URL : https://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects

반응형