Как работать с CORS, используя JAX-RS с Resteasy, Angular и Wildfly10 - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть веб-сервис, работающий в сети, на wildfly 10 на той же машине, что и мой угловой клиент.

  • Получить запрос работает
  • Put и delete вызываются 2 раза, как я понял, потому что предварительный запрос выполнен. A curl -X DELETE http://localhost:8080/resteasyWebServices-1.0-SNAPSHOT/company/57 -i хорошо работает на нем, не делая запросов дважды. Напротив, при вызове с угловым клиентом restWebService вызывается дважды!

Я пытался добавить corsFilter, но это помогло мне не только получать запросы, но и не помогло решить мою проблему

package com.solarity.app; // {{ groupId}}.app

import com.solarity.rest.CompanyRestService;
import com.solarity.rest.PersonRestService;
import org.jboss.resteasy.plugins.interceptors.CorsFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

@ApplicationPath("/")
public class InitApplication extends Application {

    /**
    * 
    */
    Set<Object> singletons;
    HashSet<Class<?>> webServiceClasses;

    public InitApplication() {
        super();
        webServiceClasses = new HashSet<>();
        webServiceClasses.add(PersonRestService.class);
        webServiceClasses.add(CompanyRestService.class);


        singletons = new LinkedHashSet<>();
        singletons.add(this.getCorsFilter());

    }

    @Override
    public Set<Class<?>> getClasses() {
        return webServiceClasses;
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }

    private CorsFilter getCorsFilter() {
        CorsFilter result = new CorsFilter();
        result.getAllowedOrigins().add("http://localhost:4200");

        return result;
    }
}

Я безуспешно пытался внедрить метод параметров в свой веб-сервис ...

package com.solarity.rest; // Note your package will be {{ groupId }}.rest

import com.solarity.entities.CompanyEntity;
import com.solarity.entities.PersonEntity;
import com.solarity.service.CompanyService;
import com.solarity.service.PersonService;
import com.solarity.util.ResponseUtil;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
* 
*
*/
@Path("/company")
public class CompanyRestService {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private CompanyService companyService = new CompanyService();



    @GET // This annotation indicates GET request
    @Path("/")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAll() {
        Object response = null;
        String errMsg = null;
        int responseStatus = -1;
        try {
            this.logger.debug("companyServcfindAll----------------debug");
            this.logger.warn("companyServcfindAll----------------WARN");
            response = companyService.findAll();
        } catch (Exception e) {
            errMsg = "Error getting all persons";
            logger.error(errMsg, e);
        }
        return ResponseUtil.getAlteredResponse(response, errMsg, responseStatus, HttpMethod.GET);
    }


    /**
    * curl -X DELETE http://localhost:8080/resteasyWebServices-1.0-SNAPSHOT/company/57 -i
    *
    * @param id
    * @return
    */
    @DELETE
    @Path("/{param}")
    public Response delete(@PathParam("param") Integer id){
        Object response = null;
        String errMsg = null;
        int responseStatus = -1;
        try {
            logger.debug("Deleting entity", id);
            companyService.delete(id);
            responseStatus = HttpStatus.SC_OK;
        } catch (Exception e) {
            errMsg = "Error Deleting Entity:" + id;
            logger.error(errMsg, e);
            response = errMsg;
            responseStatus = HttpStatus.SC_METHOD_FAILURE;
        }

        return ResponseUtil.getAlteredResponse(response, errMsg, responseStatus, HttpMethod.DELETE);
    }

    /**
    * Not working
    * @return
    */
    @OPTIONS
    @Path("{path : .*}")
    public Response options() {
        return Response.ok("")
                .header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
                .header("Access-Control-Allow-Credentials", "true")
                .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
                .header("Access-Control-Max-Age", "1209600")
                .build();
    }

}//end Class

Вот мой класс ResponseUtils

package com.solarity.util;

import org.apache.http.HttpStatus;

import javax.ws.rs.core.Response;

public class ResponseUtil {


    /**
    *
        Built to counter a Angular cross-reference problem
            Adapted for Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/dlssResteasy1-1.0-SNAPSHOT/person/getPersonsAsJSON. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
            source answer https://stackoverflow.com/questions/23450494/how-to-enable-cross-domain-requests-on-jax-rs-web-services?answertab=votes#tab-top

        More Documentation about CORS on https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS


    * @param param the object to send if errorMsg is null
    * @param errorMsg if not null sends an error code with error Message
    * @param responseStatus response status which can be found from HttpStatus.* (if <= 0 will be taken from errorMsg, or ok)
    * @return an altered response which is customized
    */
    public static Response getAlteredResponse( Object param, String errorMsg, int responseStatus, String httpMethod ) {
        Response result = null;
        int rStatus = responseStatus;
        if (errorMsg != null && responseStatus <= 0) {
            rStatus = HttpStatus.SC_UNPROCESSABLE_ENTITY;
        } else if (errorMsg == null && responseStatus <= 0){
            rStatus = HttpStatus.SC_OK;
        }
        if ( errorMsg == null ) {
            result = Response
                    .status(rStatus)
                    .entity(param)
                    .build();
        }else{
            result = Response.status(rStatus)
                    .entity(errorMsg)
                    .build();
        }
        return result;
    }

}

Вот результат отладки в FF enter image description here

Ответы [ 3 ]

0 голосов
/ 07 сентября 2018

Прежде всего, есть документация по CORS , которую мне нужно было прочитать, я не мог избежать этого, как я надеялся ...

Два звонка с Angular

Часть ответа на мою проблему была на самом деле два вызова из Angular .

Я не понял, что каждый раз, когда совершается звонок на subscribe на httpclient.put(), звонок делается!

Документация HttpClient

При вызове метода subscribe () выполняется наблюдаемое, вот что инициирует запрос DELETE.

Итак, что я сделал:

  1. Звоните methodResult = httpclient.put('someUrl', someData, someHeader).subscribe({ data => { console.log('added') });
  2. При вызове этого метода вызовите снова с abovePutMethod.subscribe( data => { doSomeThingWithComponentRefresh })

Таким образом, только ОДИН вызов для подписки решил мою проблему двойного вызова


Для остальной части протокола CORS

угловой клиент

//UrlHelper
public static putHttpRequestOptions = {
    headers: new HttpHeaders({
    'Content-Type': 'application/json',
    })
};

//Function call somewhere
const result = this.httpClient.put(url, jsonStringValues, UrlHelper.putHttpRequestOptions);

Java Resteasy сервер

// InitApplication extends Application

public InitApplication() {
    super();
    webServiceClasses = new HashSet<>();
    webServiceClasses.add(PersonRestService.class);
    webServiceClasses.add(CompanyRestService.class);


    singletons = new LinkedHashSet<>();
    singletons.add(this.getCorsFilter());

}

private CorsFilter getCorsFilter() {
    CorsFilter result = new CorsFilter();
    result.getAllowedOrigins().add("*");
    result.setAllowedMethods("OPTIONS, GET, POST, DELETE, PUT, PATCH");
    result.setCorsMaxAge(86400);//Max in FF 86400=24h https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
    //
    return result;
}


// RestWebService
@PUT
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(CompanyEntity entity ){
    Object response = null;
    String errMsg = null;
    int responseStatus = -1;
    try {
        logger.debug("Received entity", entity);
        companyService.persist(entity);
        responseStatus = HttpStatus.SC_CREATED;
    } catch (Exception e) {
        errMsg = "Error adding Entity:" + entity;
        logger.error(errMsg, e);
        response = errMsg;
        responseStatus = HttpStatus.SC_METHOD_FAILURE;
    }

    return ResponseUtil.getAlteredResponse(response, errMsg, responseStatus, HttpMethod.PUT);
}

// Called on result of all RestWebServices (I'm sure there are better/best practices, feel free to comment me this section)
/**
 * @param param the object to send if errorMsg is null
 * @param errorMsg if not null sends an error code with error Message
 * @param responseStatus response status which can be found from HttpStatus.* (if <= 0 will be taken from errorMsg, or ok)
 * @return an altered response which is customized
 */
public static Response getAlteredResponse( Object param, String errorMsg, int responseStatus, String httpMethod ) {
    Response result = null;
    int rStatus = responseStatus;
    if (errorMsg != null && responseStatus <= 0) {
        rStatus = HttpStatus.SC_UNPROCESSABLE_ENTITY;
    } else if (errorMsg == null && responseStatus <= 0){
        rStatus = HttpStatus.SC_OK;
    }
    String accessControlAllowMethods = "GET, POST, PUT, DELETE, OPTIONS, HEAD";
    if ( errorMsg == null ) {
        result = Response
                .status(rStatus)
                .header("Access-Control-Allow-Origin", "*") //TODO: fix permission here!
                .header("Access-Control-Allow-Methods", accessControlAllowMethods)
                .header("Access-Control-Max-Age", "1728000")
                .entity(param)
                .build();
    }else{
        result = Response.status(rStatus)
                .header("Access-Control-Allow-Origin", "*") //TODO: fix permission here!
                .header("Access-Control-Allow-Methods", accessControlAllowMethods)
                .header("Access-Control-Max-Age", "1728000")
                .entity(errorMsg)
                .build();
    }
    return result;
}
0 голосов
/ 07 сентября 2018

Вы можете увидеть трафик с помощью Chrome DevTools на вкладке Сеть

0 голосов
/ 06 сентября 2018
  • Соблюдайте запрос и ответ OPTIONS и убедитесь, что ответ OPTION, поступающий с сервера, содержит правильную информацию. Он сообщает клиенту, что сервер принимает
  • Наблюдайте позже реальный запрос PUT GET POST и т. Д., Идущий на сервер. Имеет ли он все заголовки, которые вы хотите?
  • Вам не нужно создавать и задавать маршрут. Смотрите здесь RFC https://www.w3.org/TR/cors/

Добавьте в эту ветку копию запроса и ответа OPTIONS (не тот, который вы создали, а тот, из пакета, который вы используете, если вы не используете пакет, поищите его), чтобы увидеть, что не так с конфигурация.

Добавьте также следующий POST, GET, PUT и т. Д. Как запрос, так и ответ

...