Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem passing a custom Header with CORS #85

Open
giorgiofran opened this issue Feb 4, 2016 · 1 comment
Open

Problem passing a custom Header with CORS #85

giorgiofran opened this issue Feb 4, 2016 · 1 comment

Comments

@giorgiofran
Copy link

I was trying to pass a custom header to a CORS POST request, but I had some problems.
I set the following header inside my called apiMethod:
context.responseHeaders['Access-Control-Allow-Headers'] = 'origin, x-requested-with, content-type, accept, my-header';
but I still had an error.

Then I discovered that, when CORS is set, the browser first execute an OPTIONS request and then a POST.
The code I inserted above is only called during the POST Response; the OPTIONS response is handled automatically, so there is no way to let the system reply with my custom Access-Control-Allow-Header.
Even changing the defaultResponseHeader would not work, because headers are replaced entirely when when an OPTIONS response is sent (headers['access-control-allow-headers'] = 'origin, x-requested-with, content-type, accept';) in ApiConfig class.

So, I did some little changes to the code.
Now it works for my needs.
If you are interested to integrate the rpc package with the following changes feel free to do it.

The logic is that I have added a new field to the ApiMethod annotation: corsAllowHeaders.
The field is appended to the existing header only in case of OPTIONS request.
As far as I can see this is enough. The following is an example of how you can set the custom header.
The field can be a comma separated list of custom headers.
@ApiMethod(path: 'list', method: 'POST', corsAllowHeaders: 'my-header, my-header2')

Here are the code changes:

  • src/annotations.dart
  /// Description of the method.
  final String description;

  final String corsAllowHeaders;    

  const ApiMethod({this.name, this.path, this.method: 'GET',
      this.description, this.corsAllowHeaders});  
}
  • src/config/method.dart
class ApiConfigMethod {
  final Symbol symbol;
  final String id;
  final String name;
  final String path;
  final String httpMethod;
  final String description;
  final String corsAllowHeaders; 

and

ApiConfigMethod(
      this.id,
      this._instance,
      this.symbol,
      this.name,
      this.path,
      this.httpMethod,
      this.description,
      this.corsAllowHeaders,
  • src/parser.dart

    var methodConfig = new ApiConfigMethod(
        discoveryId,
        methodOwner,
        mm.simpleName,
        name,
        metadata.path,
        httpMethod,
        metadata.description,
        metadata.corsAllowHeaders,  
        pathParams,
        queryParams,
        requestSchema,
        responseSchema,
        parser)
    
  • src/config/api.dart

  Future<HttpApiResponse> handleHttpOptionsRequest(
      ParsedHttpApiRequest request) async {
    var requestedHttpMethods = request.headers['access-control-request-method'];
    List<String> allowed = [];
    ApiConfigMethod   selectedMethod;
    // If the header value is passed as a String we split the String into a List
    // and make sure the returned values are Strings (see 'Create OPTIONS
    // response' below).
    bool valueAsString = requestedHttpMethods is String;
    if (valueAsString) {
      requestedHttpMethods = requestedHttpMethods.split(',');
    }
    assert('OPTIONS'.allMatches(request.methodKey).length == 1);
    if (requestedHttpMethods != null) {
      requestedHttpMethods.forEach((String httpMethod) {
        var methodKey =
            request.methodKey.replaceFirst('OPTIONS', httpMethod.trim());
        final List<ApiConfigMethod> methods = _methodMap[methodKey];
        if (methods != null) {
          for (var method in methods) {
            if (method.matches(request)) {
              allowed.add(httpMethod);
              selectedMethod = method;
              break;
            }
          }
        }
      });
    }

    // Create OPTIONS response.
    var headers = new Map.from(defaultResponseHeaders);
    if (allowed.isNotEmpty) {
      var allowedMethods = valueAsString ? allowed.join(',') : allowed;
      headers[HttpHeaders.ALLOW] = allowedMethods;
      headers['access-control-allow-methods'] = allowedMethods;
      headers['access-control-allow-headers'] =
          'origin, x-requested-with, content-type, accept';
      if (selectedMethod.corsAllowHeaders != null &&
          selectedMethod.corsAllowHeaders.length > 0) {
        headers['access-control-allow-headers'] =
        "${headers['access-control-allow-headers']}, ${selectedMethod.corsAllowHeaders}";
      }
    }
    return new HttpApiResponse(HttpStatus.OK, null, headers);
@platelk
Copy link

platelk commented Jul 21, 2016

Another way to do this is to use shelf have http server and manually handle OPTIONS and CORS header, here is a package for shelf : https://pub.dartlang.org/packages/shelf_cors

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants