【问题标题】:How to handle CORS using JAX-RS with Resteasy, Angular and Wildfly10如何使用带有 Resteasy、Angular 和 Wildfly10 的 JAX-RS 处理 CORS
【发布时间】:2018-09-08 04:48:40
【问题描述】:

我在 Wildfly 10 上与我的 Angular 客户端在同一台机器上有一个 resteasy 网络服务。

  • 获取请求作品
  • 据我了解,Put 和 delete 被调用了 2 次,因为预检请求已完成。 curl -X DELETE http://localhost:8080/resteasyWebServices-1.0-SNAPSHOT/company/57 -i 可以很好地处理它,而无需两次请求。相反,当使用 Angular 客户端调用时,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的结果

【问题讨论】:

    标签: java cors resteasy wildfly-10


    【解决方案1】:
    • 观察 OPTIONS 请求和响应,并确保来自服务器的 OPTION 响应具有正确的信息。它告诉客户端服务器正在接受什么
    • 稍后观察真正的请求 PUT GET POST 等发送到服务器。它是否包含您想要的所有标头?
    • 您不需要创建和 OPTIONS 路由。请参阅此处的 RFC https://www.w3.org/TR/cors/

    将 OPTIONS 请求和响应的副本添加到此线程(不是您创建的那个,而是您正在使用的包中的那个,如果您不使用包,请查找一个),看看有什么问题配置。

    还添加下一个 POST、GET、PUT 等请求和响应

    【讨论】:

    • 谢谢!我在哪里可以观察到这一点?是wireshark的唯一解决方案吗?
    【解决方案2】:

    首先有一个 documentation about CORS 我必须阅读才能理解,我无法避免如我所愿...

    来自 Angular 的两个调用

    我的问题的部分答案实际上是来自 Angular 的两个调用

    我不明白每次在httpclient.put() 上调用subscribe 时,调用就结束了!

    HttpClient Documentation

    调用 subscribe() 方法执行 observable,这就是 发起 DELETE 请求。

    所以我做的是:

    1. 致电methodResult = httpclient.put('someUrl', someData, someHeader).subscribe({ data =&gt; { console.log('added') });
    2. 在此方法的调用者上再次使用abovePutMethod.subscribe( data =&gt; { doSomeThingWithComponentRefresh })

    所以只调用一次订阅解决了我的两次调用问题


    对于 CORS 协议的其余部分

    Angular 客户端

    //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;
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用 Chrome DevTools 在“网络”选项卡中查看流量

      【讨论】:

      • 一切正常吗?你为 Java 使用了什么包?在 python Flask 中我使用了 flask-cors 。
      • cors 自带 resteasy。目前一切都按预期......
      • 很高兴听到这个消息
      猜你喜欢
      • 2016-08-20
      • 1970-01-01
      • 2013-01-13
      • 2016-09-11
      • 2018-12-08
      • 2016-07-23
      • 2014-02-03
      相关资源
      最近更新 更多