utf-8をフォームからJAX-RS(REST)に転送する際の問題

はじめに


Webアプリケーションインターフェイスからデータを転送する方法はいくつかありますが、おそらく最も一般的なのは、MIMEタイプのapplication/x-www-form-urlencodedフォームを送信することです。 別のオプションはmultipart/form-dataです。

MVCフレームワークのコントローラーは、受信用のサーバーテクノロジーとして使用できます( Spring MVCJava Server PagesJava Server Facesは、主要なJavaテクノロジーから言及する必要があります。しかし、これらのフレームワークは、Javaまたはニーズフレームワークが許可するものから離れたステップ。RESTインターフェースをアプリケーションのバックエンドに公開する場合、フロントエンド開発は単純化されます。 onizatora選択肢が大幅に拡大: Apacheの速度FreeMarkerのトゥルービュー/コントローラに関連付けされ、その後、サーバー側でフォームデータをビーナで書かれているが(Spirng MVCはよく最新に統合することを言及する価値がある)、JSFはまた、遺伝的問題を観察しました。..エンコーディングについては、別の記事のトピックを検討します。

JAX-RSの簡単な紹介は、 前の記事で説明しました 。 また、JAX-RSを介して公開されるインターフェースは、フォームデータを使用してGETおよびPOSTリクエストを受け入れることができます。 この記事では、非latin-1を使用する場合のこのアプローチの問題について説明します。


JAX-RSでのフォームデータの取得


フォームからメソッドにパラメーターを注入するためのアノテーションがいくつかあります: @Form@FormParamPOST要求の場合、 @FormParam GET in動作の@FormParam同等です。 小さな例外を除いて:utf-8使用時の動作。 GETメソッドを使用する場合、 urlencodedから文字列へのデコードは問題なく発生します。 POSTContent-Typecharset=utf-8を指定するPOSTも必要です。これにより、 urlencodedからデコードするときurlencoded 、バイトストリームがLatin-1ではなくUTF-8に変換されます(デフォルトの動作)。

ブラウザ

ブラウザ(またはjqueryなど)はエンコーディングを指定しないため、古いUTF-8 CPが含まれている場合、フォームフィールドの値が誤って解釈されます。 Content-Type: application/x-www-form-urlencoded; charset=utf-8ヘッダーを明示的に指定することにより、jqueryを使用する場合、この問題は回避されます。application Content-Type: application/x-www-form-urlencoded; charset=utf-8 Content-Type: application/x-www-form-urlencoded; charset=utf-8 。 jsのないフォームの場合、フォームにenctypeを指定して成功しませんでした。

Jboss resteasy

もちろん、疑問が生じます。データが他のアプリケーションから取得される場合は、 Form注釈を使用する必要がありますか? 私たちの場合、その使用はDRYの原則から正当化されます。同じメソッドをクライアントフォームとこのAPIを使用する外部アプリケーションの両方で使用できます。

Resteasyクライアントフレームワークを使用する場合、いくつかのオプションが可能です。 たとえば、必要なメソッドに@HeaderParam("Content-Type")を追加できます。

 @Path("/") public interface Rest { @POST @Path("test") public String test(@FormParam("q") String query, @HeaderParam("Content-Type") String contentType); } 


使用法は次のようになります。

 Rest client = ProxyFactory.create(Rest.class, url); client.test(query, "application/x-www-form-urlencode; charset=utf-8"); 


ただし、 Content-Type: application/x-www-form-urlencodedヘッダーにcharsetフィールドを追加するクライアントインターセプターを使用する方がより正確で便利です。 次のように実装されます。

 @ClientInterceptor @HeaderDecoratorPrecedence public class RestInterceptor implements ClientExecutionInterceptor { public static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded"; public static final String FORM_CONTENT_TYPE_WITH_CHARSET = "application/x-www-form-urlencoded; charset=utf-8"; @Override public ClientResponse execute(ClientExecutionContext context) throws Exception { String contentType = context.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); if (formWithoutCharset(contentType)) { context.getRequest().header(HttpHeaders.CONTENT_TYPE, FORM_CONTENT_TYPE_WITH_CHARSET); } return context.proceed(); } private boolean formWithoutCharset(String contentType) { return contentType != null && contentType.contains(FORM_CONTENT_TYPE) && ! contentType.contains("charset"); } } 


もちろん、インターセプターのこのバリアントは理想的ではありません。 latin-1フォームを送信したい可能性があります...しかし、これはクライアントコードを簡素化する最初のステップです。

有効にするには、フレームワークの初期化手順をわずかに修正する必要があります。

 public static void initResteasy() { ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance(); RegisterBuiltin.register(factory); InterceptorRegistry<ClientExecutionInterceptor> registry = factory.getClientExecutionInterceptorRegistry(); registry.register(new RestInterceptor()); } 


その後、 ClientRequestを使用するとき、およびリクエストの送信プロセスでプロキシオブジェクトを使用するとき、 application/x-www-form-urlencodedContent-Typeヘッダーがあり、 charsetがない場合、それを含むヘッダーが付加されます。

Source: https://habr.com/ru/post/J140270/


All Articles