2012-12-20 5 views
57

wdrożyć składnik Web-Services do JBoss Application Server 7 pomocą RESTEasy JAX-RS realizację.Wymagane @QueryParam w JAX-RS (i co robić w przypadku ich braku)

Czy istnieje adnotacja dostępny zadeklarować wymagane obowiązkowe @QueryParam parametry JAX-RS? A jeśli nie, jaki jest "standardowy" sposób radzenia sobie z sytuacjami, w których brakuje takich parametrów?

Moje metody usług sieciowych (zasobów) zwracają wyniki w postaci JSON, jeśli są poprawnie wywoływane z wszystkimi obowiązkowymi argumentami, ale nie jestem pewien, jaki jest najlepszy sposób wskazania dzwoniącemu, że brakuje wymaganego parametru.

+4

Można dodać adnotację '@ DefaultValue' i ustawić parametr na odpowiednią wartość, gdy jej brakuje. Jeśli nie możesz mieć wartości domyślnej, a parametr jest naprawdę ważny, być może powinieneś sprawdzić parametr "null" i zwrócić kod statusu "400 żądania". – toniedzwiedz

Odpowiedz

62

Dobre pytanie. Niestety (a może na szczęście) nie ma mechanizmu w JAX-RS, aby wprowadzić jakiekolwiek parametry. Jeśli parametr nie zostanie podany, jego wartość będzie wynosić NULL, a twój zasób powinien się nim odpowiednio zająć. Polecam używać WebApplicationException do informowania swoich użytkowników:

@GET 
@Path("/some-path") 
public String read(@QueryParam("name") String name) { 
    if (name == null) { 
    throw new WebApplicationException(
     Response.status(HttpURLConnection.HTTP_BAD_REQUEST) 
     .entity("name parameter is mandatory") 
     .build() 
    ); 
    } 
    // continue with a normal flow 
} 
+12

[Dokumentacja dla JAX-RS 1.0] (https://wikis.oracle.com/display/Jersey/Overview++JAX-RS+1.0+Features) mówi, że nie zawsze będzie to zero. Będzie to "pusta kolekcja dla List, Set lub SortedSet, null dla innych typów obiektów i zdefiniowana w Javie wartość domyślna dla typów pierwotnych." – hotshot309

+2

'String' nie jest typem pierwotnym, więc jest" puste dla innych typów obiektów " – yegor256

+10

Sugeruje również, aby nie używać HttpURLConnection.HTTP_BAD_REQUEST, ale raczej javax.ws.rs.core.Response.Status.BAD_REQUEST, aby zachować zgodność z metodą oczekiwane parametry. – cmonkey

45

Można użyć javax.validation adnotacje do wyegzekwowania, że ​​parametry są obowiązkowe przez opisywanie ich @javax.validation.constraints.NotNull. Zobacz an example for Jersey i one for RESTeasy.

Więc metoda będzie po prostu stać:

@GET 
@Path("/some-path") 
public String read(@NotNull @QueryParam("name") String name) { 
    String something = 
    // implementation 
    return something; 
} 

Uwaga że wyjątek zostanie następnie przetłumaczony przez dostawcę JAX-RS do jakiegoś kodu błędu. Zwykle można go zastąpić, rejestrując własną implementację javax.ws.rs.ext.ExceptionMapper<javax.validation.ValidationException>.

Zapewnia to scentralizowany sposób tłumaczenia obowiązkowego parametru na odpowiedzi na błędy i nie jest konieczne powielanie kodu.

+11

Jednym z problemów związanych z tym podejściem jest to, że komunikat o błędzie nie określa nazwy nieobecnego parametru, coś takiego jak "arg1 nie może być puste". Szczęśliwie Bean Validation wprowadził interfejs javax.validation.ParameterNameProvider. W przypadku JAX-RS możemy użyć adnotacji QueryParam i PathParam, aby uzyskać nazwy (ponieważ odbicie nie pozwala na pobieranie nazw parametrów). Przykład można znaleźć tutaj: http://stackoverflow.com/q/22496527/998772 –

+0

Tak, przeszedłem przez ten ból i zapytałem [pytanie na ten temat] (http://stackoverflow.com/questions/22567097/jersey- sprawdzanie poprawności fasoli - parameternameprovider). Jest to wykonalne, po prostu trochę więcej kodu do napisania. –

+0

Próbuję zrobić coś podobnego, ale @NotNull nie wykrywa go, nawet jeśli pominę ten parametr zapytania z adresu URL. Rozpocząłem wątek tutaj [link] (http://stackoverflow.com/questions/25737837/notnull-annotation-is-not-checking-null-queryparameter-in-jersey-rest-resource) – PST

14

wpadłem na ten sam problem i postanowił, że nie chcą szablonowe gazillion zerowe kontrole rozrzuconych po moim kodu REST, więc to jest to, co postanowiłem zrobić:

  1. Tworzenie adnotacji, które powoduje wyjątek, który zostanie zgłoszony, gdy nie zostanie podany wymagany parametr.
  2. Obchodź wyrzucony wyjątek w taki sam sposób, w jaki radzę sobie z wszystkimi innymi wyjątkami wyrzuconymi w moim kodzie REST.

Dla 1), I wdrożone następujące adnotacje:

import java.lang.annotation.Documented; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Target(ElementType.PARAMETER) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
public @interface Required 
{ 
    // This is just a marker annotation, so nothing in here. 
} 

... i następujące JAX-RS ContainerRequestFilter aby je egzekwować:

import java.lang.reflect.Parameter; 
import javax.ws.rs.QueryParam; 
import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.container.ContainerRequestFilter; 
import javax.ws.rs.container.ResourceInfo; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.ext.Provider; 

@Provider 
public class RequiredParameterFilter implements ContainerRequestFilter 
{ 
    @Context 
    private ResourceInfo resourceInfo; 

    @Override 
    public void filter(ContainerRequestContext requestContext) 
    { 
     // Loop through each parameter 
     for (Parameter parameter : resourceInfo.getResourceMethod().getParameters()) 
     { 
      // Check is this parameter is a query parameter 
      QueryParam queryAnnotation = parameter.getAnnotation(QueryParam.class); 

      // ... and whether it is a required one 
      if (queryAnnotation != null && parameter.isAnnotationPresent(Required.class)) 
      { 
       // ... and whether it was not specified 
       if (!requestContext.getUriInfo().getQueryParameters().containsKey(queryAnnotation.value())) 
       { 
        // We pass the query variable name to the constructor so that the exception can generate a meaningful error message 
        throw new YourCustomRuntimeException(queryAnnotation.value()); 
       } 
      } 
     } 
    } 
} 

Musisz się zarejestrować ContainerRequestFilter w ten sam sposób, w jaki rejestrujesz inne klasy @Provider z biblioteką JAX-RS. Może RESTEasy robi to automatycznie.

Dla 2), obsłużyć wszystkie wyjątki w czasie wykonywania przy użyciu JAX-RS generyczne ExceptionMapper:

import javax.ws.rs.core.Response; 
import javax.ws.rs.ext.ExceptionMapper; 
import javax.ws.rs.ext.Provider; 

@Provider 
public class MyExceptionMapper implements ExceptionMapper<RuntimeException> 
{ 
    @Override 
    public Response toResponse(RuntimeException ex) 
    { 
     // In this example, we just return the .toString() of the exception. 
     // You might want to wrap this in a JSON structure if this is a JSON API, for example. 
     return Response 
      .status(Response.Status.BAD_REQUEST) 
      .entity(ex.toString()) 
      .build(); 
    } 
} 

Tak jak poprzednio, należy zarejestrować klasę z biblioteki JAX-RS.

+1

Czy to oferuje coś, @ javax.validation.constraints.NotNull nie zrobiłby? –

+1

@MichaelHaefele Zachowuje nazwę parametru, co jest przydatne przy wyświetlaniu znaczącego komunikatu o błędzie. Nazwa parametru zostanie utracona, jeśli użyjesz adnotacji 'NotNull', co jest niefortunne. Właśnie to sprawiło, że napisałem własną adnotację. Ale zobacz także https://stackoverflow.com/questions/13968261/required-queryparam-in-jax-rs-and-what-to-do-in-their-absence/31893626?noredirect=1#comment35934905_21920512. Być może rzeczy się zmieniły, odkąd napisałem ten kod. – Zero3

Powiązane problemy