web-dev-qa-db-ja.com

JSONをREST APIに投稿する

JSONリクエストを受け付けるREST APIを作成しています。

私はCURLを使用してテストしています:

curl -i -POST -H 'Accept: application/json' -d '{"id":1,"pan":11111}' http://localhost:8080/PurchaseAPIServer/api/purchase


しかし、次のエラーが表示されます:

HTTP/1.1 415 Unsupported Media Type
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 1051
Date: Wed, 25 Apr 2012 21:36:14 GMT

The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().



デバッグ時には、コントローラーで作成アクションに進むことさえありません。

import Java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.app.model.Purchase;
import com.app.service.IPurchaseService;

@Controller
public class PurchaseController {

    @Autowired
    private IPurchaseService purchaseService;

    @RequestMapping(value = "purchase", method = RequestMethod.GET)
    @ResponseBody
    public final List<Purchase> getAll() {
        return purchaseService.getAll();
    }

    @RequestMapping(value = "purchase", method = RequestMethod.POST)
    @ResponseStatus( HttpStatus.CREATED )
    public void create(@RequestBody final Purchase entity) {
        purchaseService.addPurchase(entity);
    }
}



更新

Jacksonの構成をAppConfig.Javaに追加しました。

@Configuration
@ComponentScan(basePackages = "com.app")
public class AppConfig {

    @Bean
    public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter()
    {
        final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter();
        final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();

        HttpMessageConverter<?>[] httpMessageConverter = { mappingJacksonHttpMessageConverter };

        String[] supportedHttpMethods = { "POST", "GET", "HEAD" };

        annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter);
        annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods);

        return annotationMethodHandlerAdapter;
    }
}



私のGETは現在正しく機能しています。

curl -i -H "Content-Type:application/json" -H "Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 26 Apr 2012 21:19:55 GMT

[{"id":1,"pan":111}]



しかし、POSTを試みると次のメッセージが表示されます。

curl -i -X POST -H "Content-Type:application/json" -H "Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchaseMe -d "{"id":2,"pan":122}"

HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 971
Date: Thu, 26 Apr 2012 21:29:56 GMT
Connection: close

The request sent by the client was syntactically incorrect ().



私のモデル:

@Entity
@XmlRootElement
public class Purchase implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 6603477834338392140L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private Long pan;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getPan() {
        return pan;
    }

    public void setPan(Long pan) {
        this.pan = pan;
    }

}



間違っているアイデアはありますか?

ありがとう

16
Thomas Buckley

sdouglass が示唆するように、Spring MVCは自動的にJacksonを検出し、JSONとの変換を処理するMappingJacksonHttpMessageConverterを設定します。しかし、彼が指摘したように、コンバータを動作させるには明示的にコンバータを構成する必要がありました。

以下を追加しましたが、CURL GETリクエストは機能していました。

AppConfig.Java

@Configuration
@ComponentScan(basePackages = "com.app")
public class AppConfig {

    @Bean
    public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter()
    {
        final AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter();
        final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();

        HttpMessageConverter<?>[] httpMessageConverter = { mappingJacksonHttpMessageConverter };

        String[] supportedHttpMethods = { "POST", "GET", "HEAD" };

        annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter);
        annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods);

        return annotationMethodHandlerAdapter;
    }
}


curl -i -H "Content-Type:application/json" -H "Accept:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 26 Apr 2012 21:19:55 GMT

[{"id":1,"pan":111}]



しかし、次のCURL POSTはまだ動作していませんでした(コントローラーアクションを押して、コンソールデバッグ情報を表示しないでください。

curl -i -X POST -H "Content-Type:application/json"  http://localhost:8080/PurchaseAPIServer/api/purchaseMe -d "{"id":2,"pan":122}"

HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 971
Date: Thu, 26 Apr 2012 21:29:56 GMT
Connection: close

The request sent by the client was syntactically incorrect ().



そこで、詳細なデバッグを開始するために Logback を追加しました。

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>/home/thomas/springApps/purchaseapi.log</file>
        <encoder>
            <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <logger name="org.hibernate" level="DEBUG" />

    <logger name="org.springframework" level="TRACE" />
    <logger name="org.springframework.transaction" level="INFO" />
    <logger name="org.springframework.security" level="INFO" /> <!-- to debug security related issues (DEBUG) -->
    <logger name="org.springframework.web.servlet.mvc" level="TRACE" /> <!-- some serialization issues are at trace level here: org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod -->

    <!-- our service -->
    <logger name="com.app" level="DEBUG" />
    <!-- <logger name="com.app" level="INFO" /> --><!-- to follow if setup is being executed -->

    <root level="INFO">
        <appender-ref ref="FILE" />
    </root>

</configuration>



TRACEレベルのデバッグをorg.springframework.web.servlet.mvcに追加すると、問題に対する答えが得られました。

2012-04-28 14:17:44,579 DEBUG [http-bio-8080-exec-3] o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor [AbstractMessageConverterMethodArgumentResolver.Java:117] Reading [com.app.model.Purchase] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@74a14fed]
2012-04-28 14:17:44,604 TRACE [http-bio-8080-exec-3] o.s.w.s.m.m.a.ServletInvocableHandlerMethod [InvocableHandlerMethod.Java:159] Error resolving argument [0] [type=com.app.model.Purchase]
HandlerMethod details: 
Controller [com.app.controller.PurchaseController]
Method [public void com.app.controller.PurchaseController.create(com.app.model.Purchase)]

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unexpected character ('p' (code 112)): was expecting double-quote to start field name



CURL POSTを次のように変更し、すべて機能しました:

curl -i -X POST -H "Content-Type:application/json" http://localhost:8080/PurchaseAPIServer/api/purchase -d '{"pan":11111}'
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Sat, 28 Apr 2012 13:19:40 GMT

うまくいけば、誰かがこれを役に立つと思います。

16
Thomas Buckley

私が正しく思い出すと、Spring MVCはクラスパスでジャクソンを自動的に検出し、JSONへの/からの変換を処理するためにMappingJacksonHttpMessageConverterを設定するとSpringのドキュメントに記載されていますが、働くもの。これをMVC設定XMLに追加してみてください:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        </list>
    </property>
</bean>

[〜#〜] update [〜#〜]:これに加えて、投稿されるJSONを適切にフォーマットします。 https:// stackoverflow.com/a/10363876/433789

6
sdouglass

その2014年と私は、同じ問題を解決するのに役立つこの質問にいくつかの更新を追加したかった。

  1. Spring 3.2で非推奨のAnnotationMethodHandlerAdapterを置き換えるコードの更新

     @Configuration 
     public class AppConfig {
     
    
        @Bean
        public RequestMappingHandlerAdapter  annotationMethodHandlerAdapter()
        {
            final RequestMappingHandlerAdapter annotationMethodHandlerAdapter = new RequestMappingHandlerAdapter();
            final MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJackson2HttpMessageConverter();
    
            List<HttpMessageConverter<?>> httpMessageConverter = new ArrayList<HttpMessageConverter<?>>();
            httpMessageConverter.add(mappingJacksonHttpMessageConverter);
    
            String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
    
            annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverter);
            annotationMethodHandlerAdapter.setSupportedMethods(supportedHttpMethods);
    
            return annotationMethodHandlerAdapter;
        }
    }
    
  2. HTTP/1.1 415サポートされていないメディアタイプエラー

正しいJSON構成を追加した後でも415エラーが発生する理由を把握しようとして多くの時間を費やした後、問題はサーバー側ではなくクライアント側にあることが最終的にわかりました。 SpringがJSONを受け入れるためには、httpヘッダーの一部として「Content-Type:application/json」と「Accept:application/json」の両方を送信していることを確認する必要があります。具体的には、Android application HttpUrlConnectionであり、次のように設定する必要がありました。

    public static String doPost(final String urlString,final String requestBodyString) throws IOException {
        final URL url = new URL(urlString);

        final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        try {
          urlConnection.setReadTimeout(10000 /* milliseconds */);
          urlConnection.setConnectTimeout(15000 /* milliseconds */);
          urlConnection.setRequestProperty("Content-Type", "application/json");
          urlConnection.setRequestProperty("Accept", "application/json");
          urlConnection.setDoOutput(true);
          urlConnection.setRequestMethod("POST");
          urlConnection.setChunkedStreamingMode(0);

          urlConnection.connect();

          final PrintWriter out = new PrintWriter(urlConnection.getOutputStream());
          out.print(requestBodyString);
          out.close();

          final InputStream in = new BufferedInputStream(urlConnection.getInputStream());
          final String response =  readIt(in);

          in.close(); //important to close the stream

          return response;

        } finally {
          urlConnection.disconnect();
        }
    }
5
yoram givon

POSTリクエストに含まれているものの記述子を追加してみてください。つまり、curlにヘッダーを追加します。

_Content-Type: application/json
_

追加しない場合、curlは、実際に送信するものに関係なく、デフォルトの_text/html_を使用します。

また、PurchaseController.create()では、受け入れられる型が_application/json_であることを追加する必要があります。

2
Diego Sevilla

Yoram givonの答えに似た単体テストソリューションを次に示します- https://stackoverflow.com/a/22516235/1019307

_public class JSONFormatTest
{
    MockMvc mockMvc;

    // The controller used doesn't seem to be important though YMMV
    @InjectMocks
    ActivityController controller;  

    @Before
    public void setup()
    {
        MockitoAnnotations.initMocks(this);

        this.mockMvc = standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter())
                .build();
    }

    @Test
    public void thatSaveNewDataCollectionUsesHttpCreated() throws Exception
    {
        String jsonContent = getHereJSON02();
        this.mockMvc
                .perform(
                     post("/data_collections").content(jsonContent).contentType(MediaType.APPLICATION_JSON)
                                .accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isCreated());
    }

    private String getHereJSON01()
    {
        return "{\"dataCollectionId\":0,\"name\":\"Sat_016\",\"type\":\"httpUploadedFiles\"," ...
    }
}
_

単体テストを実行すると、print()は例外を含むMockHttpServletRequestを出力するはずです。

Eclipseで(他のIDEでこれを行う方法がわからない)、[例外]リンクをクリックすると、その例外のプロパティダイアログが開きます。その例外を解除するには、「有効」ボックスにチェックマークを付けます。

単体テストをデバッグすると、Eclipseは例外で中断します。問題を明らかにする必要があります。私の場合、JSONに同じエンティティが2つあったためです。

2
HankCa

私は同じ問題を抱えていましたが、コードの2つの変更によって解決されました:

  1. メソッド引数に@PathVariableがありません。メソッドには何もありませんでした
  2. ハンドラーインターセプターで使用していたものが非推奨になり、問題が発生したため、SpringConfigクラスの次のメソッド:

    public RequestMappingHandlerAdapter RequestMappingHandlerAdapter()
    {
        final RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();
        final MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
        final String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
    
        requestMappingHandlerAdapter.getMessageConverters().add(0, mappingJacksonHttpMessageConverter);
        requestMappingHandlerAdapter.setSupportedMethods(supportedHttpMethods);
    
        return requestMappingHandlerAdapter;
    }
    
2
Nisarg Panchal

同じ問題があり、解決しました。

1そのスレッドで説明されているようにMappingJackson2HttpMessageConverterを追加します(セクション4も参照してください http://www.baeldung.com/spring-httpmessageconverter-rest

2正しいコマンドを使用します(エスケープ記号付き):

curl -i -X POST -H "Content-Type:application/json" -d "{\"id\":\"id1\",\"password\":\"password1\"}" http://localhost:8080/user    
0
Alex Alekhin

私は一度経験し、最終的にjarファイルjackson-mapper-asl.jarを追加することでそれを解決しました。これらのすべての依存関係を含めたかどうかを確認してください。ただし、例外自体はそれを伝えません。

また、Beanを明示的に構成する必要はなく、@ RequestMappingステートメントに「消費」を入れる必要もありません。 Spring 3.1 btwを使用しています。

contentType: "application/json"のみが構成する必要があります。はい、クライアント側。

0
tonywu

アプリの構成に次のコードを追加してみてください

<mvc:annotation-driven>
  <mvc:message-converters>
      <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
          <property name="objectMapper" ref="jacksonObjectMapper" />
      </bean>
  </mvc:message-converters>
0
Roberto