Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 13
  1. #1

    Aiuto su sessioni in Grizzly e JAX-WS

    ciao!

    ho creato un rest serivce con jax-ws, e come server sto usando grizzly.
    ho vari "path" a seconda di cosa interrogo, ecc.

    adesso ho creato questo path per il download di un file excel:
    codice:
    @Path("excel")
    public class ExcelService {
    
        @GET
        @Path("/get")
        @Produces("application/vnd.ms-excel")
        public Response download() throws IOException {
            File file = File.createTempFile("export", ".xls");
            ResponseBuilder response = Response.ok((Object) file);
            response.header("Content-Disposition", "attachment; filename=" + file);
            file.deleteOnExit();
            return response.build();
        }
    
    }
    diciamo che funziona, nel senso che mi crea un file e poi me lo fa scaricare (ovviamente dovrei dargli una sistemata...).
    però vorrei riempire il file con l'ultima query eseguita.
    quindi pensavo:
    - l'utente richiama un endpoint
    - viene salvata una variabile di sessione affinchè il programma si ricordi l'ultima query eseguita
    - quando richiamo questo endpoint, creo il file excel prendendo i dati dall'ultima query

    secondo voi quella delle sessioni può essere una buona strada?
    sennò avete qualche altro consiglio??

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    ho creato un rest serivce
    Hai messo "get" nel path, quindi non è proprio secondo la filosofia e le convenzioni REST.

    Quote Originariamente inviata da fermat Visualizza il messaggio
    secondo voi quella delle sessioni può essere una buona strada?
    No, non proprio. Se una API REST la si sviluppa "stateless" ... è meglio. In questo caso per stateless si intende che non si dovrebbe tenere sù una "sessione" lato server. In altre parole, tutto ciò che serve per interpretare e soddisfare una request, deve essere passato dal client, senza che ci debba essere già un qualche "stato" presente sul server.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  3. #3
    Quote Originariamente inviata da andbin Visualizza il messaggio
    Hai messo "get" nel path, quindi non è proprio secondo la filosofia e le convenzioni REST.


    No, non proprio. Se una API REST la si sviluppa "stateless" ... è meglio. In questo caso per stateless si intende che non si dovrebbe tenere sù una "sessione" lato server. In altre parole, tutto ciò che serve per interpretare e soddisfare una request, deve essere passato dal client, senza che ci debba essere già un qualche "stato" presente sul server.
    ciao andbin!

    per quanto riguarda il get, ho notato che se lo levo non mi funziona più.
    ad esempio:
    codice:
    @Path("zona")
    public class ZonaService {
    
        // @GET
        @Path("/zone/")
        @Produces(MediaType.APPLICATION_JSON)
        public ArrayList<Zona> getAll() {
            ZonaQueries query = new ZonaQueries();
            ArrayList<Zona> list = null;
            try {
                list = query.getAll();
            } catch (ClassNotFoundException | SQLException | MappableException ex) {
                throw new NotFoundException(new JsonError("Errore", ex.getMessage()));
            }
            return list;
        }
    }
    se lo apro con un browser, mi da pagina bianca.
    se lo decommento, invece, vedo il json a video.

    per quanto riguarda il resto, ho perfettamente compreso.
    ma se ad esempio volessi dare la possibilità di scaricare in excel i risultati di cui sopra, dovrei duplicare questo metodo?
    oppure che alternativa avrei??

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    per quanto riguarda il get, ho notato che se lo levo non mi funziona più.
    Non l'annotazione @GET (quella ovviamente serve) ma il "/get" che avevi messo nel path.

    REST si basa su convenzioni e su una filosofia ben precisa. Anche e soprattutto riguardo gli URL da usare. L'obiettivo principale è quello di far sì che gli URL rappresentino delle "risorse".

    Es.

    GET http://blabla.com/api/books (=lista tutti i libri)
    POST http://blabla.com/api/books (=inserisce nuovo libro)
    GET http://blabla.com/api/books/1234 (=dettaglio del libro Id 1234)
    PUT http://blabla.com/api/books/1234 (=aggiorna il libro Id 1234)
    DELETE http://blabla.com/api/books/1234 (=elimina il libro Id 1234)

    come vedi non c'è un "get" nel path. Il GET è la "azione" ed è il metodo HTTP.

    Quote Originariamente inviata da fermat Visualizza il messaggio
    ma se ad esempio volessi dare la possibilità di scaricare in excel i risultati di cui sopra, dovrei duplicare questo metodo?
    Innanzitutto bisogna vedere chi è il "client". Un browser? Una applicazione "desktop"?
    Comunque se a parità di url vuoi fornire rappresentazioni differenti (es. un excel o un pdf piuttosto che un JSON) una opzione è lavorare con i media type. Se è il client che fornisce Accept:application/json tu gli dai un JSON, se fornisce Accept:application/pdf gli dai un pdf, ecc... Ma qui ripeto, dipende anche da chi è il client, da cui deriva la possibilità di controllare gli header o no.

    In alternativa sfruttare delle estensioni per rappresentazioni particolari.

    http://blabla.com/api/books/1234 (JSON standard)
    http://blabla.com/api/books/1234.pdf (un pdf)

    Non è il massimo ma si fa anche ...
    Ultima modifica di andbin; 28-03-2017 a 09:39
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  5. #5
    Quote Originariamente inviata da andbin Visualizza il messaggio
    Non l'annotazione @GET (quella ovviamente serve) ma il "/get" che avevi messo nel path.

    REST si basa su convenzioni e su una filosofia ben precisa. Anche e soprattutto riguardo gli URL da usare. L'obiettivo principale è quello di far sì che gli URL rappresentino delle "risorse".

    Es.

    GET http://blabla.com/api/books (=lista tutti i libri)
    POST http://blabla.com/api/books (=inserisce nuovo libro)
    GET http://blabla.com/api/books/1234 (=dettaglio del libro Id 1234)
    PUT http://blabla.com/api/books/1234 (=aggiorna il libro Id 1234)
    DELETE http://blabla.com/api/books/1234 (=elimina il libro Id 1234)

    come vedi non c'è un "get" nel path. Il GET è la "azione" ed è il metodo HTTP.
    ah ok ok.
    no vabbè, negli altri path il /get non c'è.
    quella è una prova volante per vedere se funzionava il download del file.

    Innanzitutto bisogna vedere chi è il "client". Un browser? Una applicazione "desktop"?
    Comunque se a parità di url vuoi fornire rappresentazioni differenti (es. un excel o un pdf piuttosto che un JSON) una opzione è lavorare con i media type. Se è il client che fornisce Accept:application/json tu gli dai un JSON, se fornisce Accept:application/pdf gli dai un pdf, ecc... Ma qui ripeto, dipende anche da chi è il client, da cui deriva la possibilità di controllare gli header o no.

    In alternativa sfruttare delle estensioni per rappresentazioni particolari.

    http://blabla.com/api/books/1234 (JSON standard)
    http://blabla.com/api/books/1234.pdf (un pdf)

    Non è il massimo ma si fa anche ...
    allora, in generale, il client è un browser che interroga i service tramite ajax per prelevare i dati, e metterli su uno altro db.
    nel caso specifico dell'excel, sarebbe sempre un browser, ma ancora devo capire se userei ajax o no.
    cmq sempre un browser!

  6. #6
    allora, usare l'estensione non mi piace un granchè.
    sto provando a passare il tipo direttamente nel path.
    questa cosa qui funziona, però vorrei migliorare il codice:
    codice:
    @Path("test")
    public class TestService {
    
        @GET
        @Path("{mediaType}")
        public Response test(@PathParam("mediaType") String mediaType) {
    
            ZonaQueries query = new ZonaQueries();
            ResponseBuilder response;
            ArrayList<Zona> list = null;
            try {
                list = query.getAll();
            } catch (ClassNotFoundException | SQLException | MappableException ex) {
                throw new NotFoundException(new JsonError("Errore", ex.getMessage()));
            }
    
            if (mediaType.equals("json")) {
                response = Response.ok(list, MediaType.APPLICATION_JSON);
            } else if (mediaType.equals("xls")) {
                try {
                    File file = File.createTempFile("export", ".xls");
                    response = Response.ok((Object) file, "application/vnd.ms-excel");
                    response.header("Content-Disposition", "attachment; filename=" + file);
                    file.deleteOnExit();
                } catch (IOException ex) {
                    throw new NotFoundException(ex.getMessage());
                }
            } else {
                throw new NotFoundException("Formato non supportato!");
            }
    
            return response.build();
    
            // http://192.168.1.30:8080/cr/test/xls
            // http://192.168.1.30:8080/cr/test/json
        }
    
    }
    se gli passo come ultimo argomento xls, mi scarica il file (vuoto al momento, me è l'ultimo dei problemi).
    se gli passo json, vedo il json esattamente come negli altri service.
    quindi diciamo che funziona.

    ma:
    - non è il massimo della felssibilità nel caso in cui dovessi aggiungere altri media type
    - anche qui try/catch così non mi piacciono

    avete qualche suggerimento al riguardo??

  7. #7
    ho pensato che forse un enum poteva andare:

    codice:
    public enum MediaTypeAccepted {
        JSON("application/json"),
        XLS("application/vnd.ms-excel");
    
        private String value;
    
        MediaTypeAccepted(String value) {
            this.value = value;
        }
    
        public String getValue() {
            return value;
        }
    }
    poi:
    codice:
    @Path("test")
    public class TestService {
    
        @GET
        @Path("{mediaType}")
        public Response test(@PathParam("mediaType") String mediaType) {
            ZonaQueries query = new ZonaQueries();
            ResponseBuilder response = null;
            ArrayList<Zona> list = null;
    
            try {
                list = query.getAll();
                if (EnumUtils.isValidEnum(MediaTypeAccepted.class, mediaType.toUpperCase())) {
                    String mt = MediaTypeAccepted.valueOf(mediaType.toUpperCase()).getValue();
    
                    if (mediaType.equals("json")) {
                        response = Response.ok(list, mt);
                    } else if (mediaType.equals("xls")) {
                        File file = File.createTempFile("export", ".xls");
                        response = Response.ok((Object) file, mt);
                        response.header("Content-Disposition", "attachment; filename=" + file);
                        file.deleteOnExit();
                    }
                    
                } else {
                    throw new NotFoundException(new JsonError("Errore", "File non supportato"));
                }
            } catch (ClassNotFoundException | SQLException | MappableException | IOException ex) {
                throw new NotFoundException(new JsonError("Errore", ex.getMessage()));
            }
    
            return response.build();
        }
    
    }
    funzionare funziona.
    però:
    - in generale non so se è una buona strada
    - cmq non so ancora bene come strutturare la parte in cui faccio il controllo sul tipo di media, e in base a quello decido che operazione fare (quindi mando il json, o creo il file xls e lo faccio scaricare)

  8. #8
    ho fatto queste modifiche, e pare funzionare tutto (poi dal punto di vista design non so).

    nell'enum imposto anche l'estensione del file da scaricare (in futuro magari aggiungo altre estensioni), e il tipo di operazione (r=lettura, quindi visualizzazione, r=scrittura quindi mi fa il download):
    codice:
    public enum MediaTypeAccepted {
        JSON("application/json", "", "r"),
        XLS("application/vnd.ms-excel", "xls", "w");
    
        private String value;
        private String ext;
        private String operation;
    
        MediaTypeAccepted(String value, String ext, String operation) {
            this.value = value;
            this.ext = ext;
            this.operation = operation;
        }
    
        public String getValue() {
            return value;
        }
    
        public String getExt() {
            return ext;
        }
    
        public String getOperation() {
            return operation;
        }
    }
    poi ho creato una classe che mi crea la Response.ResponseBuilder:
    codice:
    public class CreateResponse {
    
        private Object entity;
        private String mediaType;
        private String ext;
        private String operation;
    
        public CreateResponse(Object entity, String mediaType, String ext, String operation) {
            this.entity = entity;
            this.mediaType = mediaType;
            this.ext = ext;
            this.operation = operation;
        }
    
        public Response.ResponseBuilder create() throws IOException {
            Response.ResponseBuilder response = null;
            if (operation.equals("w")) {
                File file = File.createTempFile("export", "." + ext);
                response = Response.ok((Object) file, mediaType);
                response.header("Content-Disposition", "attachment; filename=" + file);
                file.deleteOnExit();
            } else {
                response = Response.ok(entity, mediaType);
            }
            return response;
        }
    }
    a seconda del tipo di operazione creo il file o meno (ovviamente la creazione prendendo i dati devo ancora impostarla).

    infine nel service:
    codice:
    @Path("test")
    public class TestService {
    
        @GET
        @Path("{mediaType}")
        public Response test(@PathParam("mediaType") String mediaType) {
            CreateResponse response;
            Response.ResponseBuilder rb;
            if (EnumUtils.isValidEnum(MediaTypeAccepted.class, mediaType.toUpperCase())) {
                ZonaQueries query = new ZonaQueries();
                ArrayList<Zona> list = null;
                String mt = MediaTypeAccepted.valueOf(mediaType.toUpperCase()).getValue();
                String ext = MediaTypeAccepted.valueOf(mediaType.toUpperCase()).getExt();
                String operation = MediaTypeAccepted.valueOf(mediaType.toUpperCase()).getOperation();
                try {
                    list = query.getAll();
                    response = new CreateResponse(list, mt, ext, operation);
                    rb = response.create();
                } catch (ClassNotFoundException | SQLException | IOException ex) {
                    throw new NotFoundException(new JsonError("Errore", ex.getMessage()));
                }
            } else {
                throw new NotFoundException(new JsonError("Errore", "File non supportato"));
            }
            return rb.build();
        }
    
    }
    funzionare funziona.....

  9. #9
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    funzionare funziona.....
    Sì ma è molto "dispersivo". Comunque, ripeto, un conto è sfruttare la Content Negotiation "standard" che è fatta tramite il header "Accept" (che è il modo standard che un client usa per dire cosa accetta come content-type). In questo caso non c'è bisogno di alcun giro strano lato server. Basta sfruttare il @Produces avendo metodi differenti (ciascuno per un formato differente) con appunto @Produces differenti. La "negoziazione" a questo livello è fatta "a gratis" dalla implementazione di JAX-RS.

    Se invece fai una Content Negotiation "fai-da-te" (con path-param, query-param, header custom, estensione .qualcosa) .... beh, è un altro discorso ed è appunto tutto a tuo carico.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  10. #10
    ok, però ho un dubbio.
    se uso il @Produces, in sostanza dovrei avere praticamente lo stesso metodo moltiplicato per quanto formati voglio supportare, corretto?

    ma se ho già parecchi path diversi (uno per gli articoli, uno per clienti, uno per le giacenze di magazzino, ecc....) dovrei moltiplicare tutti metodi per tutti i tipi supportati.
    non è un pò una ripetizione??

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.